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)
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;
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;
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)
217 TIME_ZONE_INFORMATION sysTzi;
218 GetTimeZoneInformation(&sysTzi);
220 const auto winIds = availableWindowsIds();
222 if (equalTzi(getRegistryTzi(winId, &
ok), sysTzi))
230QDate calculateTransitionLocalDate(
const SYSTEMTIME &
rule,
int year)
233 if (
rule.wMonth == 0)
244 const int dayOfWeek =
rule.wDayOfWeek == 0 ? 7 :
rule.wDayOfWeek;
253 adjust += (
rule.wDay < 1 ? 1 :
rule.wDay > 4 ? 5 :
rule.wDay) * 7;
272 if (daySinceEpoch < 0 && msInDay > 0) {
278 return qMulOverflow(daySinceEpoch, std::integral_constant<qint64, MSECS_PER_DAY>(), &dayms)
282qint64 calculateTransitionForYear(
const SYSTEMTIME &
rule,
int year,
int bias)
302bool isAtStartOfYear(
const SYSTEMTIME &transition,
int year)
313 return transition.wMonth == 1 && transition.wDay == 1
314 && (
QDate(year, 1, 1).
dayOfWeek() - transition.wDayOfWeek) % 7 == 0
315 && transition.wHour == 0 && transition.wMinute == 0 && transition.wSecond == 0;
318struct TransitionTimePair
324 bool fakesDst =
false;
326 TransitionTimePair(
const QWinTimeZonePrivate::QWinTransitionRule &
rule,
327 int year,
int oldYearOffset)
329 :
std(calculateTransitionForYear(
rule.standardTimeRule, year,
330 rule.standardTimeBias +
rule.daylightTimeBias)),
332 dst(calculateTransitionForYear(
rule.daylightTimeRule, year,
rule.standardTimeBias))
379 if (
rule.standardTimeBias +
rule.daylightTimeBias == oldYearOffset
380 && isAtStartOfYear(
rule.daylightTimeRule, year)) {
384 if (
rule.standardTimeBias == oldYearOffset
385 && isAtStartOfYear(
rule.standardTimeRule, year)) {
387 "Year with (DST bias zero and) both transitions fake !");
393 bool startsInDst()
const
405 bool beforeInitialDst(
int year,
qint64 millis)
const
411 const QWinTimeZonePrivate *tzp,
bool isDst)
const
413 const auto type = isDst ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
423int yearEndOffset(
const QWinTimeZonePrivate::QWinTransitionRule &
rule,
int year)
431 if (pair.dst > pair.std)
438 const GEOID
id = GetUserGeoID(GEOCLASS_NATION);
440 const int size = GetGeoInfo(
id, GEO_ISO2, code, 3, 0);
448 if (rules.
last().startYear <= year)
449 return rules.
count() - 1;
451 if (rules.
first().startYear > year)
455 int lo = 0, hi = rules.
count();
458 while (lo + 1 < hi) {
459 const int mid = (lo + hi) / 2;
462 const int midYear = rules.
at(mid).startYear;
465 else if (midYear < year)
476QWinTimeZonePrivate::QWinTimeZonePrivate()
483QWinTimeZonePrivate::QWinTimeZonePrivate(
const QByteArray &ianaId)
489QWinTimeZonePrivate::QWinTimeZonePrivate(
const QWinTimeZonePrivate &
other)
491 m_displayName(
other.m_displayName), m_standardName(
other.m_standardName),
492 m_daylightName(
other.m_daylightName), m_tranRules(
other.m_tranRules)
496QWinTimeZonePrivate::~QWinTimeZonePrivate()
500QWinTimeZonePrivate *QWinTimeZonePrivate::clone()
const
502 return new QWinTimeZonePrivate(*
this);
505void QWinTimeZonePrivate::init(
const QByteArray &ianaId)
508 m_windowsId = windowsSystemZoneId();
509 m_id = systemTimeZoneId();
511 m_windowsId = ianaIdToWindowsId(ianaId);
514 const auto initialYear = [](
const QWinTransitionRule &
rule) {
519 return (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0
523 bool badMonth =
false;
524 if (!m_windowsId.isEmpty()) {
529 if (baseKey.isValid()) {
531 m_displayName = baseKey.stringValue(L
"Display");
532 m_standardName = baseKey.stringValue(L
"Std");
533 m_daylightName = baseKey.stringValue(L
"Dlt");
535 const QString dynamicKeyPath = baseKeyPath +
"\\Dynamic DST"_L1;
537 if (dynamicKey.isValid()) {
539 const auto startYear = dynamicKey.dwordValue(L
"FirstEntry");
540 const auto endYear = dynamicKey.dwordValue(L
"LastEntry");
541 for (
int year =
int(startYear.first); year <= int(endYear.first); ++year) {
543 QWinTransitionRule
rule =
544 readRegistryRule(dynamicKey,
549 && (m_tranRules.isEmpty()
550 || !isSameRule(m_tranRules.last(),
rule))) {
552 && (
rule.standardTimeRule.wMonth == 0)
553 != (
rule.daylightTimeRule.wMonth == 0)) {
555 qWarning(
"MS registry TZ API violated its wMonth constraint;"
556 "this may cause mistakes for %s from %d",
559 const TransitionTimePair pair(
rule, year,
rule.standardTimeBias);
562 = m_tranRules.size() || pair.fakesDst ? year : initialYear(
rule);
563 m_tranRules.append(
rule);
569 QWinTransitionRule
rule = readRegistryRule(baseKey, L
"TZI", &ruleOk);
572 m_tranRules.append(
rule);
579 if (m_tranRules.size() == 0) {
582 m_displayName.clear();
583 }
else if (m_id.isEmpty()) {
584 m_id = m_standardName.toUtf8();
588QString QWinTimeZonePrivate::comment()
const
590 return m_displayName;
593QString QWinTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
594 QTimeZone::NameType nameType,
600 if (nameType == QTimeZone::OffsetName) {
601 const QWinTransitionRule &
rule =
604 if (timeType == QTimeZone::DaylightTime)
606 return isoOffsetFormat(
offset * -60);
610 case QTimeZone::DaylightTime :
611 return m_daylightName;
612 case QTimeZone::GenericTime :
613 return m_displayName;
614 case QTimeZone::StandardTime :
615 return m_standardName;
617 return m_standardName;
620QString QWinTimeZonePrivate::abbreviation(
qint64 atMSecsSinceEpoch)
const
622 return data(atMSecsSinceEpoch).abbreviation;
625int QWinTimeZonePrivate::offsetFromUtc(
qint64 atMSecsSinceEpoch)
const
627 return data(atMSecsSinceEpoch).offsetFromUtc;
630int QWinTimeZonePrivate::standardTimeOffset(
qint64 atMSecsSinceEpoch)
const
632 return data(atMSecsSinceEpoch).standardTimeOffset;
635int QWinTimeZonePrivate::daylightTimeOffset(
qint64 atMSecsSinceEpoch)
const
637 return data(atMSecsSinceEpoch).daylightTimeOffset;
640bool QWinTimeZonePrivate::hasDaylightTime()
const
642 return hasTransitions();
645bool QWinTimeZonePrivate::isDaylightTime(
qint64 atMSecsSinceEpoch)
const
647 return (
data(atMSecsSinceEpoch).daylightTimeOffset != 0);
653 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
654 ruleIndex >= 0; --ruleIndex) {
655 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
657 if (year <
rule.startYear
658 || !(
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0)) {
660 return ruleToData(
rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
663 int prior = year == 1 ? -1 : year - 1;
664 const int endYear =
qMax(
rule.startYear, prior);
665 while (year >= endYear) {
666 const int newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
667 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
668 : yearEndOffset(
rule, prior);
669 const TransitionTimePair pair(
rule, year, newYearOffset);
671 if (ruleIndex == 0 && pair.beforeInitialDst(year, forMSecsSinceEpoch)) {
675 }
else if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) {
676 isDst = pair.std < pair.dst && pair.dst <= forMSecsSinceEpoch;
677 }
else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) {
681 prior = year == 1 ? -1 : year - 1;
684 return ruleToData(
rule, forMSecsSinceEpoch,
685 isDst ? QTimeZone::DaylightTime :
QTimeZone::StandardTime,
696bool QWinTimeZonePrivate::hasTransitions()
const
698 for (
const QWinTransitionRule &
rule : m_tranRules) {
699 if (
rule.standardTimeRule.wMonth > 0 &&
rule.daylightTimeRule.wMonth > 0)
708 int newYearOffset = invalidSeconds();
709 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
710 ruleIndex < m_tranRules.count(); ++ruleIndex) {
711 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
713 if (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0) {
714 int prior = year == 1 ? -1 : year - 1;
715 if (newYearOffset == invalidSeconds()) {
718 newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
719 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
720 : yearEndOffset(
rule, prior);
722 if (year <
rule.startYear) {
726 TransitionTimePair pair(
rule,
rule.startYear, newYearOffset);
732 const int endYear = ruleIndex + 1 < m_tranRules.count()
733 ?
qMin(m_tranRules.at(ruleIndex + 1).startYear, year + 2) : (year + 2);
734 while (year < endYear) {
735 const TransitionTimePair pair(
rule, year, newYearOffset);
737 Q_ASSERT(invalidMSecs() <= afterMSecsSinceEpoch);
738 if (ruleIndex == 0 && pair.beforeInitialDst(year, afterMSecsSinceEpoch)) {
747 }
else if (pair.std > afterMSecsSinceEpoch) {
748 isDst = pair.std > pair.dst && pair.dst > afterMSecsSinceEpoch;
749 }
else if (pair.dst > afterMSecsSinceEpoch) {
752 newYearOffset =
rule.standardTimeBias;
753 if (pair.dst > pair.std)
754 newYearOffset +=
rule.daylightTimeBias;
757 year = year == -1 ? 1 : year + 1;
761 return pair.ruleToData(
rule,
this, isDst);
768 if (newYearOffset == invalidSeconds())
769 newYearOffset =
rule.standardTimeBias;
780 if (beforeMSecsSinceEpoch <= minMSecs())
784 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
785 ruleIndex >= 0; --ruleIndex) {
786 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
789 if (year >=
rule.startYear
790 && (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0)) {
791 int prior = year == 1 ? -1 : year - 1;
792 const int endYear =
qMax(
rule.startYear, prior);
793 while (year >= endYear) {
794 const int newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
795 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
796 : yearEndOffset(
rule, prior);
797 const TransitionTimePair pair(
rule, year, newYearOffset);
802 if (ruleIndex == 0 && pair.beforeInitialDst(year, beforeMSecsSinceEpoch - 1))
803 return ruleToData(
rule, minMSecs(), QTimeZone::StandardTime,
false);
806 if (pair.std != invalidMSecs() && pair.std < beforeMSecsSinceEpoch) {
807 isDst = pair.std < pair.dst && pair.dst < beforeMSecsSinceEpoch;
808 }
else if (pair.dst != invalidMSecs() && pair.dst < beforeMSecsSinceEpoch) {
812 prior = year == 1 ? -1 : year - 1;
815 return pair.ruleToData(
rule,
this, isDst);
818 }
else if (ruleIndex == 0) {
822 return ruleToData(
rule, minMSecs(), QTimeZone::StandardTime,
false);
824 if (year >=
rule.startYear) {
825 year =
rule.startYear - 1;
834QByteArray QWinTimeZonePrivate::systemTimeZoneId()
const
837 const QByteArray windowsId = windowsSystemZoneId();
841 ianaId = windowsIdToDefaultIanaId(windowsId, territory);
844 ianaId = windowsIdToDefaultIanaId(windowsId);
851 const auto winIds = availableWindowsIds();
853 result += windowsIdToIanaIds(winId);
861 QTimeZone::TimeType
type,
865 tran.atMSecsSinceEpoch = atMSecsSinceEpoch;
866 tran.standardTimeOffset =
rule.standardTimeBias * -60;
868 tran.daylightTimeOffset = 0;
869 tran.abbreviation = m_standardName;
871 if (
type == QTimeZone::DaylightTime)
872 tran.standardTimeOffset +=
rule.daylightTimeBias * -60;
873 }
else if (
type == QTimeZone::DaylightTime) {
874 tran.daylightTimeOffset =
rule.daylightTimeBias * -60;
875 tran.abbreviation = m_daylightName;
877 tran.daylightTimeOffset = 0;
878 tran.abbreviation = m_standardName;
880 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...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
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...
const_reference at(qsizetype i) const noexcept
qsizetype count() const noexcept
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
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
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static const qint64 invalidData
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]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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[]