QTimeZone - Add ICU support

Add ICU backend for QTimeZone

Change-Id: I92e53a848477e366591102064b093e936f0b49d2
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
John Layt 2013-01-30 16:04:23 +00:00 committed by The Qt Project
parent 48e2c3ac33
commit 9c2cbee2b1
5 changed files with 702 additions and 1 deletions

View File

@ -52,13 +52,21 @@ QT_BEGIN_NAMESPACE
// Create default time zone using appropriate backend
static QTimeZonePrivate *newBackendTimeZone()
{
#if defined QT_USE_ICU
return new QIcuTimeZonePrivate();
#else
return new QUtcTimeZonePrivate();
#endif // QT_USE_ICU
}
// Create named time zone using appropriate backend
static QTimeZonePrivate *newBackendTimeZone(const QByteArray &olsenId)
{
#if defined QT_USE_ICU
return new QIcuTimeZonePrivate(olsenId);
#else
return new QUtcTimeZonePrivate(olsenId);
#endif // QT_USE_ICU
}
class QTimeZoneSingleton

View File

@ -0,0 +1,504 @@
/****************************************************************************
**
** Copyright (C) 2013 John Layt <jlayt@kde.org>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qtimezone.h"
#include "qtimezoneprivate_p.h"
#include <unicode/ucal.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
/*
Private
ICU implementation
*/
// ICU utilities
// Convert TimeType and NameType into ICU UCalendarDisplayNameType
static UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType, QTimeZone::NameType nameType)
{
// TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType
switch (nameType) {
case QTimeZone::ShortName :
case QTimeZone::OffsetName :
if (timeType == QTimeZone::DaylightTime)
return UCAL_SHORT_DST;
// Includes GenericTime
return UCAL_SHORT_STANDARD;
case QTimeZone::DefaultName :
case QTimeZone::LongName :
if (timeType == QTimeZone::DaylightTime)
return UCAL_DST;
// Includes GenericTime
return UCAL_STANDARD;
}
return UCAL_STANDARD;
}
// Qt wrapper around ucal_getDefaultTimeZone()
static QByteArray ucalDefaultTimeZoneId()
{
int32_t size = 30;
QString result(size, Qt::Uninitialized);
UErrorCode status = U_ZERO_ERROR;
// size = ucal_getDefaultTimeZone(result, resultLength, status)
size = ucal_getDefaultTimeZone(reinterpret_cast<UChar *>(result.data()), size, &status);
// If overflow, then resize and retry
if (status == U_BUFFER_OVERFLOW_ERROR) {
result.resize(size);
status = U_ZERO_ERROR;
size = ucal_getDefaultTimeZone(reinterpret_cast<UChar *>(result.data()), size, &status);
}
// If successful on first or second go, resize and return
if (U_SUCCESS(status)) {
result.resize(size);
return result.toUtf8();
}
return QByteArray();
}
// Qt wrapper around ucal_getTimeZoneDisplayName()
static QString ucalTimeZoneDisplayName(UCalendar *ucal, QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QString &localeCode)
{
int32_t size = 50;
QString result(size, Qt::Uninitialized);
UErrorCode status = U_ZERO_ERROR;
// size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status)
size = ucal_getTimeZoneDisplayName(ucal,
ucalDisplayNameType(timeType, nameType),
localeCode.toUtf8(),
reinterpret_cast<UChar *>(result.data()),
size,
&status);
// If overflow, then resize and retry
if (status == U_BUFFER_OVERFLOW_ERROR) {
result.resize(size);
status = U_ZERO_ERROR;
size = ucal_getTimeZoneDisplayName(ucal,
ucalDisplayNameType(timeType, nameType),
localeCode.toUtf8(),
reinterpret_cast<UChar *>(result.data()),
size,
&status);
}
// If successful on first or second go, resize and return
if (U_SUCCESS(status)) {
result.resize(size);
return result;
}
return QString();
}
// Qt wrapper around ucal_get() for offsets
static bool ucalOffsetsAtTime(UCalendar *m_ucal, qint64 atMSecsSinceEpoch,
int *utcOffset, int *dstOffset)
{
*utcOffset = 0;
*dstOffset = 0;
// Clone the ucal so we don't change the shared object
UErrorCode status = U_ZERO_ERROR;
UCalendar *ucal = ucal_clone(m_ucal, &status);
if (!U_SUCCESS(status))
return false;
// Set the date to find the offset for
status = U_ZERO_ERROR;
ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
int32_t utc = 0;
if (U_SUCCESS(status)) {
status = U_ZERO_ERROR;
// Returns msecs
utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
}
int32_t dst = 0;
if (U_SUCCESS(status)) {
status = U_ZERO_ERROR;
// Returns msecs
dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
}
ucal_close(ucal);
if (U_SUCCESS(status)) {
*utcOffset = utc;
*dstOffset = dst;
return true;
}
return false;
}
// ICU Draft api in v50, should be stable in ICU v51. Available in C++ api from ICU v3.8
#if U_ICU_VERSION_MAJOR_NUM == 50
// Qt wrapper around qt_ucal_getTimeZoneTransitionDate & ucal_get
static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal,
UTimeZoneTransitionType type,
qint64 atMSecsSinceEpoch)
{
QTimeZonePrivate::Data tran = QTimeZonePrivate::invalidData();
// Clone the ucal so we don't change the shared object
UErrorCode status = U_ZERO_ERROR;
UCalendar *ucal = ucal_clone(m_ucal, &status);
if (!U_SUCCESS(status))
return tran;
// Set the date to find the transition for
status = U_ZERO_ERROR;
ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
// Find the transition time
UDate tranMSecs = 0;
status = U_ZERO_ERROR;
bool ok = ucal_getTimeZoneTransitionDate(ucal, type, &tranMSecs, &status);
// Set the transition time to find the offsets for
if (U_SUCCESS(status) && ok) {
status = U_ZERO_ERROR;
ucal_setMillis(ucal, tranMSecs, &status);
}
int32_t utc = 0;
if (U_SUCCESS(status) && ok) {
status = U_ZERO_ERROR;
utc = ucal_get(ucal, UCAL_ZONE_OFFSET, &status) / 1000;
}
int32_t dst = 0;
if (U_SUCCESS(status) && ok) {
status = U_ZERO_ERROR;
dst = ucal_get(ucal, UCAL_DST_OFFSET, &status) / 1000;
}
ucal_close(ucal);
if (!U_SUCCESS(status) || !ok)
return tran;
tran.atMSecsSinceEpoch = tranMSecs;
tran.offsetFromUtc = utc + dst;
tran.standardTimeOffset = utc;
tran.daylightTimeOffset = dst;
// TODO No ICU API, use short name instead
if (dst == 0)
tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::StandardTime,
QTimeZone::ShortName, QLocale().name());
else
tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::DaylightTime,
QTimeZone::ShortName, QLocale().name());
return tran;
}
#endif // U_ICU_VERSION_SHORT
// Convert a uenum to a QList<QByteArray>
static QSet<QByteArray> uenumToIdSet(UEnumeration *uenum)
{
QSet<QByteArray> set;
int32_t size = 0;
UErrorCode status = U_ZERO_ERROR;
// TODO Perhaps use uenum_unext instead?
QByteArray result = uenum_next(uenum, &size, &status);
while (U_SUCCESS(status) && !result.isEmpty()) {
set << result;
status = U_ZERO_ERROR;
result = uenum_next(uenum, &size, &status);
}
return set;
}
// Qt wrapper around ucal_getDSTSavings()
static int ucalDaylightOffset(const QByteArray &id)
{
UErrorCode status = U_ZERO_ERROR;
const int32_t dstMSecs = ucal_getDSTSavings(reinterpret_cast<const UChar *>(id.data()), &status);
if (U_SUCCESS(status))
return (dstMSecs / 1000);
else
return 0;
}
// Create the system default time zone
QIcuTimeZonePrivate::QIcuTimeZonePrivate()
: m_ucal(0)
{
// TODO No ICU C API to obtain sysem tz, assume default hasn't been changed
init(ucalDefaultTimeZoneId());
}
// Create a named time zone
QIcuTimeZonePrivate::QIcuTimeZonePrivate(const QByteArray &olsenId)
: m_ucal(0)
{
// Need to check validity here as ICu will create a GMT tz if name is invalid
if (availableTimeZoneIds().contains(olsenId))
init(olsenId);
}
QIcuTimeZonePrivate::QIcuTimeZonePrivate(const QIcuTimeZonePrivate &other)
: QTimeZonePrivate(other), m_ucal(0)
{
// Clone the ucal so we don't close the shared object
UErrorCode status = U_ZERO_ERROR;
m_ucal = ucal_clone(other.m_ucal, &status);
if (!U_SUCCESS(status)) {
m_id.clear();
m_ucal = 0;
}
}
QIcuTimeZonePrivate::~QIcuTimeZonePrivate()
{
ucal_close(m_ucal);
}
QTimeZonePrivate *QIcuTimeZonePrivate::clone()
{
return new QIcuTimeZonePrivate(*this);
}
void QIcuTimeZonePrivate::init(const QByteArray &olsenId)
{
m_id = olsenId;
const QString id = QString::fromUtf8(m_id);
UErrorCode status = U_ZERO_ERROR;
//TODO Use UCAL_GREGORIAN for now to match QLocale, change to UCAL_DEFAULT once full ICU support
m_ucal = ucal_open(reinterpret_cast<const UChar *>(id.data()), id.size(),
QLocale().name().toUtf8(), UCAL_GREGORIAN, &status);
if (!U_SUCCESS(status)) {
m_id.clear();
m_ucal = 0;
}
}
QString QIcuTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
// Return standard offset format name as ICU C api doesn't support it yet
if (nameType == QTimeZone::OffsetName) {
const Data nowData = data(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
// We can't use transitions reliably to find out right dst offset
// Instead use dst offset api to try get it if needed
if (timeType == QTimeZone::DaylightTime)
return isoOffsetFormat(nowData.standardTimeOffset + ucalDaylightOffset(m_id));
else
return isoOffsetFormat(nowData.standardTimeOffset);
}
return ucalTimeZoneDisplayName(m_ucal, timeType, nameType, locale.name());
}
QString QIcuTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
{
// TODO No ICU API, use short name instead
if (isDaylightTime(atMSecsSinceEpoch))
return displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, QString());
else
return displayName(QTimeZone::StandardTime, QTimeZone::ShortName, QString());
}
int QIcuTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const
{
int stdOffset = 0;
int dstOffset = 0;
ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
return stdOffset + dstOffset;
}
int QIcuTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const
{
int stdOffset = 0;
int dstOffset = 0;
ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
return stdOffset;
}
int QIcuTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
{
int stdOffset = 0;
int dstOffset = 0;
ucalOffsetsAtTime(m_ucal, atMSecsSinceEpoch, &stdOffset, & dstOffset);
return dstOffset;
}
bool QIcuTimeZonePrivate::hasDaylightTime() const
{
// TODO No direct ICU C api, work-around below not reliable? Find a better way?
return (ucalDaylightOffset(m_id) != 0);
}
bool QIcuTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
{
// Clone the ucal so we don't change the shared object
UErrorCode status = U_ZERO_ERROR;
UCalendar *ucal = ucal_clone(m_ucal, &status);
if (!U_SUCCESS(status))
return false;
// Set the date to find the offset for
status = U_ZERO_ERROR;
ucal_setMillis(ucal, atMSecsSinceEpoch, &status);
bool result = false;
if (U_SUCCESS(status)) {
status = U_ZERO_ERROR;
result = ucal_inDaylightTime(ucal, &status);
}
ucal_close(ucal);
return result;
}
QTimeZonePrivate::Data QIcuTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
// Available in ICU C++ api, and draft C api in v50
// TODO When v51 released see if api is stable
QTimeZonePrivate::Data data = invalidData();
#if U_ICU_VERSION_MAJOR_NUM == 50
data = ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE,
forMSecsSinceEpoch);
#else
ucalOffsetsAtTime(m_ucal, forMSecsSinceEpoch, &data.standardTimeOffset,
&data.daylightTimeOffset);
data.offsetFromUtc = data.standardTimeOffset + data.daylightTimeOffset;
data.abbreviation = abbreviation(forMSecsSinceEpoch);
#endif // U_ICU_VERSION_MAJOR_NUM == 50
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data;
}
bool QIcuTimeZonePrivate::hasTransitions() const
{
// Available in ICU C++ api, and draft C api in v50
// TODO When v51 released see if api is stable
#if U_ICU_VERSION_MAJOR_NUM == 50
return true;
#else
return false;
#endif // U_ICU_VERSION_MAJOR_NUM == 50
}
QTimeZonePrivate::Data QIcuTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const
{
// Available in ICU C++ api, and draft C api in v50
// TODO When v51 released see if api is stable
#if U_ICU_VERSION_MAJOR_NUM == 50
return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_NEXT, afterMSecsSinceEpoch);
#else
Q_UNUSED(afterMSecsSinceEpoch)
return invalidData();
#endif // U_ICU_VERSION_MAJOR_NUM == 50
}
QTimeZonePrivate::Data QIcuTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
// Available in ICU C++ api, and draft C api in v50
// TODO When v51 released see if api is stable
#if U_ICU_VERSION_MAJOR_NUM == 50
return ucalTimeZoneTransition(m_ucal, UCAL_TZ_TRANSITION_PREVIOUS, beforeMSecsSinceEpoch);
#else
Q_UNUSED(beforeMSecsSinceEpoch)
return invalidData();
#endif // U_ICU_VERSION_MAJOR_NUM == 50
}
QByteArray QIcuTimeZonePrivate::systemTimeZoneId() const
{
// No ICU C API to obtain sysem tz
// TODO Assume default hasn't been changed and is the latests system
return ucalDefaultTimeZoneId();
}
QSet<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds() const
{
UErrorCode status = U_ZERO_ERROR;
UEnumeration *uenum = ucal_openTimeZones(&status);
QSet<QByteArray> set;
if (U_SUCCESS(status))
set = uenumToIdSet(uenum);
uenum_close(uenum);
return set;
}
QSet<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(QLocale::Country country) const
{
QByteArray regionCode = QLocalePrivate::countryToCode(country).toUtf8();
UErrorCode status = U_ZERO_ERROR;
UEnumeration *uenum = ucal_openCountryTimeZones(regionCode, &status);
QSet<QByteArray> set;
if (U_SUCCESS(status))
set = uenumToIdSet(uenum);
uenum_close(uenum);
return set;
}
QSet<QByteArray> QIcuTimeZonePrivate::availableTimeZoneIds(int offsetFromUtc) const
{
// TODO Available directly in C++ api but not C api, from 4.8 onwards new filter method works
#if U_ICU_VERSION_MAJOR_NUM >= 49 || (U_ICU_VERSION_MAJOR_NUM == 4 && U_ICU_VERSION_MINOR_NUM == 8)
UErrorCode status = U_ZERO_ERROR;
UEnumeration *uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, 0,
&offsetFromUtc, &status);
QSet<QByteArray> set;
if (U_SUCCESS(status))
set = uenumToIdSet(uenum);
uenum_close(uenum);
return set;
#else
return QTimeZonePrivate::availableTimeZoneIds(offsetFromUtc);
#endif
}
QT_END_NAMESPACE

View File

@ -57,6 +57,10 @@
#include "qtimezone.h"
#include "qlocale_p.h"
#ifdef QT_USE_ICU
#include <unicode/ucal.h>
#endif // QT_USE_ICU
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QTimeZonePrivate : public QSharedData
@ -193,6 +197,49 @@ private:
QString m_comment;
};
#ifdef QT_USE_ICU
class Q_AUTOTEST_EXPORT QIcuTimeZonePrivate Q_DECL_FINAL : public QTimeZonePrivate
{
public:
// Create default time zone
QIcuTimeZonePrivate();
// Create named time zone
QIcuTimeZonePrivate(const QByteArray &olsenId);
QIcuTimeZonePrivate(const QIcuTimeZonePrivate &other);
~QIcuTimeZonePrivate();
QTimeZonePrivate *clone();
QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
const QLocale &locale) const Q_DECL_OVERRIDE;
QString abbreviation(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
int offsetFromUtc(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
int standardTimeOffset(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
int daylightTimeOffset(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
bool hasDaylightTime() const Q_DECL_OVERRIDE;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const Q_DECL_OVERRIDE;
Data data(qint64 forMSecsSinceEpoch) const Q_DECL_OVERRIDE;
bool hasTransitions() const Q_DECL_OVERRIDE;
Data nextTransition(qint64 afterMSecsSinceEpoch) const Q_DECL_OVERRIDE;
Data previousTransition(qint64 beforeMSecsSinceEpoch) const Q_DECL_OVERRIDE;
QByteArray systemTimeZoneId() const Q_DECL_OVERRIDE;
QSet<QByteArray> availableTimeZoneIds() const Q_DECL_OVERRIDE;
QSet<QByteArray> availableTimeZoneIds(QLocale::Country country) const Q_DECL_OVERRIDE;
QSet<QByteArray> availableTimeZoneIds(int offsetFromUtc) const Q_DECL_OVERRIDE;
private:
void init(const QByteArray &olsenId);
UCalendar *m_ucal;
};
#endif // QT_USE_ICU
QT_END_NAMESPACE
#endif // QTIMEZONEPRIVATE_P_H

View File

@ -139,7 +139,8 @@ contains(QT_CONFIG, zlib) {
contains(QT_CONFIG,icu) {
SOURCES += tools/qlocale_icu.cpp \
tools/qcollator_icu.cpp
tools/qcollator_icu.cpp \
tools/qtimezoneprivate_icu.cpp
DEFINES += QT_USE_ICU
win32 {
CONFIG(static, static|shared) {

View File

@ -60,9 +60,13 @@ private slots:
void windowsId();
// Backend tests
void utcTest();
void icuTest();
private:
void printTimeZone(const QTimeZone tz);
#ifdef QT_BUILD_INTERNAL
void testCetPrivate(const QTimeZonePrivate &tzp);
#endif // QT_BUILD_INTERNAL
bool debug;
};
@ -496,5 +500,142 @@ void tst_QTimeZone::utcTest()
#endif // QT_BUILD_INTERNAL
}
void tst_QTimeZone::icuTest()
{
#if defined(QT_BUILD_INTERNAL) && defined(QT_USE_ICU)
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
// Test default constructor
QIcuTimeZonePrivate tzpd;
QVERIFY(tzpd.isValid());
// Test invalid constructor
QIcuTimeZonePrivate tzpi("Gondwana/Erewhon");
QCOMPARE(tzpi.isValid(), false);
// Test named constructor
QIcuTimeZonePrivate tzp("Europe/Berlin");
QVERIFY(tzp.isValid());
// Only test names in debug mode, names used can vary by ICU version installed
if (debug) {
// Test display names by type
QLocale enUS("en_US");
QCOMPARE(tzp.displayName(QTimeZone::StandardTime, QTimeZone::LongName, enUS),
QString("Central European Standard Time"));
QCOMPARE(tzp.displayName(QTimeZone::StandardTime, QTimeZone::ShortName, enUS),
QString("GMT+01:00"));
QCOMPARE(tzp.displayName(QTimeZone::StandardTime, QTimeZone::OffsetName, enUS),
QString("UTC+01:00"));
QCOMPARE(tzp.displayName(QTimeZone::DaylightTime, QTimeZone::LongName, enUS),
QString("Central European Summer Time"));
QCOMPARE(tzp.displayName(QTimeZone::DaylightTime, QTimeZone::ShortName, enUS),
QString("GMT+02:00"));
QCOMPARE(tzp.displayName(QTimeZone::DaylightTime, QTimeZone::OffsetName, enUS),
QString("UTC+02:00"));
// ICU C api does not support Generic Time yet, C++ api does
QCOMPARE(tzp.displayName(QTimeZone::GenericTime, QTimeZone::LongName, enUS),
QString("Central European Standard Time"));
QCOMPARE(tzp.displayName(QTimeZone::GenericTime, QTimeZone::ShortName, enUS),
QString("GMT+01:00"));
QCOMPARE(tzp.displayName(QTimeZone::GenericTime, QTimeZone::OffsetName, enUS),
QString("UTC+01:00"));
// Test Abbreviations
QCOMPARE(tzp.abbreviation(std), QString("CET"));
QCOMPARE(tzp.abbreviation(dst), QString("CEST"));
}
testCetPrivate(tzp);
#endif // QT_USE_ICU
}
#ifdef QT_BUILD_INTERNAL
// Test each private produces the same basic results for CET
void tst_QTimeZone::testCetPrivate(const QTimeZonePrivate &tzp)
{
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 prev = QDateTime(QDate(2011, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
QCOMPARE(tzp.offsetFromUtc(std), 3600);
QCOMPARE(tzp.offsetFromUtc(dst), 7200);
QCOMPARE(tzp.standardTimeOffset(std), 3600);
QCOMPARE(tzp.standardTimeOffset(dst), 3600);
QCOMPARE(tzp.daylightTimeOffset(std), 0);
QCOMPARE(tzp.daylightTimeOffset(dst), 3600);
QCOMPARE(tzp.hasDaylightTime(), true);
QCOMPARE(tzp.isDaylightTime(std), false);
QCOMPARE(tzp.isDaylightTime(dst), true);
QTimeZonePrivate::Data dat = tzp.data(std);
QCOMPARE(dat.atMSecsSinceEpoch, std);
QCOMPARE(dat.offsetFromUtc, 3600);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 0);
QCOMPARE(dat.abbreviation, tzp.abbreviation(std));
dat = tzp.data(dst);
QCOMPARE(dat.atMSecsSinceEpoch, dst);
QCOMPARE(dat.offsetFromUtc, 7200);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 3600);
QCOMPARE(dat.abbreviation, tzp.abbreviation(dst));
// Only test transitions if host system supports them
if (tzp.hasTransitions()) {
QTimeZonePrivate::Data tran = tzp.nextTransition(std);
QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1332637200000);
QCOMPARE(tran.offsetFromUtc, 7200);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
tran = tzp.nextTransition(dst);
QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1351386000000);
QCOMPARE(tran.offsetFromUtc, 3600);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
tran = tzp.previousTransition(std);
QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1319936400000);
QCOMPARE(tran.offsetFromUtc, 3600);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
tran = tzp.previousTransition(dst);
QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1332637200000);
QCOMPARE(tran.offsetFromUtc, 7200);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
QTimeZonePrivate::DataList expected;
tran.atMSecsSinceEpoch = (qint64)1301752800000;
tran.offsetFromUtc = 7200;
tran.standardTimeOffset = 3600;
tran.daylightTimeOffset = 3600;
expected << tran;
tran.atMSecsSinceEpoch = (qint64)1316872800000;
tran.offsetFromUtc = 3600;
tran.standardTimeOffset = 3600;
tran.daylightTimeOffset = 0;
expected << tran;
QTimeZonePrivate::DataList result = tzp.transitions(prev, std);
QCOMPARE(result.count(), expected.count());
for (int i = 0; i > expected.count(); ++i) {
QCOMPARE(result.at(i).atMSecsSinceEpoch, expected.at(i).atMSecsSinceEpoch);
QCOMPARE(result.at(i).offsetFromUtc, expected.at(i).offsetFromUtc);
QCOMPARE(result.at(i).standardTimeOffset, expected.at(i).standardTimeOffset);
QCOMPARE(result.at(i).daylightTimeOffset, expected.at(i).daylightTimeOffset);
}
}
}
#endif // QT_BUILD_INTERNAL
QTEST_APPLESS_MAIN(tst_QTimeZone)
#include "tst_qtimezone.moc"