From 7ed7d66b5a420f70d056a95a63c9be5de1af3191 Mon Sep 17 00:00:00 2001 From: John Layt Date: Mon, 4 Feb 2013 20:54:29 +0000 Subject: QTimeZone - Add Mac backend Add Mac backend support Change-Id: Iafa2dbd925e18431f571e3eac62983015f8bc977 Reviewed-by: Thiago Macieira --- src/corelib/tools/qtimezone.cpp | 8 +- src/corelib/tools/qtimezoneprivate_mac.mm | 260 ++++++++++++++++++++++++++++++ src/corelib/tools/qtimezoneprivate_p.h | 51 ++++++ src/corelib/tools/tools.pri | 1 + 4 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 src/corelib/tools/qtimezoneprivate_mac.mm (limited to 'src/corelib/tools') diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp index 9de9e5de8b..ad13433583 100644 --- a/src/corelib/tools/qtimezone.cpp +++ b/src/corelib/tools/qtimezone.cpp @@ -59,7 +59,9 @@ static QTimeZonePrivate *newBackendTimeZone() return new QUtcTimeZonePrivate(); #endif // QT_USE_ICU #else -#if defined Q_OS_UNIX && !defined Q_OS_MAC +#if defined Q_OS_MAC + return new QMacTimeZonePrivate(); +#elif defined Q_OS_UNIX return new QTzTimeZonePrivate(); #elif defined QT_USE_ICU return new QIcuTimeZonePrivate(); @@ -79,7 +81,9 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &olsenId) return new QUtcTimeZonePrivate(olsenId); #endif // QT_USE_ICU #else -#if defined Q_OS_UNIX && !defined Q_OS_MAC +#if defined Q_OS_MAC + return new QMacTimeZonePrivate(olsenId); +#elif defined Q_OS_UNIX return new QTzTimeZonePrivate(olsenId); #elif defined QT_USE_ICU return new QIcuTimeZonePrivate(olsenId); diff --git a/src/corelib/tools/qtimezoneprivate_mac.mm b/src/corelib/tools/qtimezoneprivate_mac.mm new file mode 100644 index 0000000000..8d319aebbd --- /dev/null +++ b/src/corelib/tools/qtimezoneprivate_mac.mm @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2013 John Layt +** 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 "private/qcore_mac_p.h" +#include "qstringlist.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +/* + Private + + OS X system implementation +*/ + +// Create the system default time zone +QMacTimeZonePrivate::QMacTimeZonePrivate() +{ + init(systemTimeZoneId()); +} + +// Create a named time zone +QMacTimeZonePrivate::QMacTimeZonePrivate(const QByteArray &olsenId) + : m_nstz(0) +{ + init(olsenId); +} + +QMacTimeZonePrivate::QMacTimeZonePrivate(const QMacTimeZonePrivate &other) + : QTimeZonePrivate(other), m_nstz(0) +{ + m_nstz = [other.m_nstz copy]; +} + +QMacTimeZonePrivate::~QMacTimeZonePrivate() +{ + [m_nstz release]; +} + +QTimeZonePrivate *QMacTimeZonePrivate::clone() +{ + return new QMacTimeZonePrivate(*this); +} + +void QMacTimeZonePrivate::init(const QByteArray &olsenId) +{ + if (availableTimeZoneIds().contains(olsenId)) { + m_nstz = [NSTimeZone timeZoneWithName:QCFString::toNSString(QString::fromUtf8(olsenId))]; + if (m_nstz) + m_id = olsenId; + } +} + +QString QMacTimeZonePrivate::comment() const +{ + return QCFString::toQString([m_nstz description]); +} + +QString QMacTimeZonePrivate::displayName(QTimeZone::TimeType timeType, + QTimeZone::NameType nameType, + const QLocale &locale) const +{ + // TODO Mac doesn't support OffsetName yet so use standard offset name + if (nameType == QTimeZone::OffsetName) { + const Data nowData = data(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()); + // TODO Cheat for now, assume if has dst the offset if 1 hour + if (timeType == QTimeZone::DaylightTime && hasDaylightTime()) + return isoOffsetFormat(nowData.standardTimeOffset + 3600); + else + return isoOffsetFormat(nowData.standardTimeOffset); + } + + NSTimeZoneNameStyle style = NSTimeZoneNameStyleStandard; + + switch (nameType) { + case QTimeZone::ShortName : + if (timeType == QTimeZone::DaylightTime) + style = NSTimeZoneNameStyleShortDaylightSaving; + else if (timeType == QTimeZone::GenericTime) + style = NSTimeZoneNameStyleShortGeneric; + else + style = NSTimeZoneNameStyleShortStandard; + break; + case QTimeZone::DefaultName : + case QTimeZone::LongName : + if (timeType == QTimeZone::DaylightTime) + style = NSTimeZoneNameStyleDaylightSaving; + else if (timeType == QTimeZone::GenericTime) + style = NSTimeZoneNameStyleGeneric; + else + style = NSTimeZoneNameStyleStandard; + break; + case QTimeZone::OffsetName : + // Unreachable + break; + } + + NSString *macLocaleCode = QCFString::toNSString(locale.name()); + NSLocale *macLocale = [[NSLocale alloc] initWithLocaleIdentifier:macLocaleCode]; + const QString result = QCFString::toQString([m_nstz localizedName:style locale:macLocale]); + [macLocaleCode release]; + [macLocale release]; + return result; +} + +QString QMacTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const +{ + const NSTimeInterval seconds = atMSecsSinceEpoch / 1000.0; + return QCFString::toQString([m_nstz abbreviationForDate:[NSDate dateWithTimeIntervalSince1970:seconds]]); +} + +int QMacTimeZonePrivate::offsetFromUtc(qint64 atMSecsSinceEpoch) const +{ + const NSTimeInterval seconds = atMSecsSinceEpoch / 1000.0; + return [m_nstz secondsFromGMTForDate:[NSDate dateWithTimeIntervalSince1970:seconds]]; +} + +int QMacTimeZonePrivate::standardTimeOffset(qint64 atMSecsSinceEpoch) const +{ + return offsetFromUtc(atMSecsSinceEpoch) - daylightTimeOffset(atMSecsSinceEpoch); +} + +int QMacTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const +{ + const NSTimeInterval seconds = atMSecsSinceEpoch / 1000.0; + return [m_nstz daylightSavingTimeOffsetForDate:[NSDate dateWithTimeIntervalSince1970:seconds]]; +} + +bool QMacTimeZonePrivate::hasDaylightTime() const +{ + // TODO No Mac API, assume if has transitions + return hasTransitions(); +} + +bool QMacTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const +{ + const NSTimeInterval seconds = atMSecsSinceEpoch / 1000.0; + return [m_nstz isDaylightSavingTimeForDate:[NSDate dateWithTimeIntervalSince1970:seconds]]; +} + +QTimeZonePrivate::Data QMacTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const +{ + const NSTimeInterval seconds = forMSecsSinceEpoch / 1000.0; + Data data; + data.atMSecsSinceEpoch = forMSecsSinceEpoch; + data.offsetFromUtc = [m_nstz secondsFromGMTForDate: + [NSDate dateWithTimeIntervalSince1970:seconds]]; + data.daylightTimeOffset = [m_nstz daylightSavingTimeOffsetForDate: + [NSDate dateWithTimeIntervalSince1970:seconds]]; + data.standardTimeOffset = data.offsetFromUtc - data.daylightTimeOffset; + data.abbreviation = QCFString::toQString([m_nstz abbreviationForDate: + [NSDate dateWithTimeIntervalSince1970:seconds]]); + return data; +} + +bool QMacTimeZonePrivate::hasTransitions() const +{ + // TODO No direct Mac API, so return if has next after 1970, i.e. since start of tz + // TODO Not sure what is returned in event of no transitions, assume will be before requested date + NSDate *epoch = [NSDate dateWithTimeIntervalSince1970:0]; + const NSDate *date = [m_nstz nextDaylightSavingTimeTransitionAfterDate:epoch]; + const bool result = ([date timeIntervalSince1970] > [epoch timeIntervalSince1970]); + [epoch release]; + [date release]; + return result; +} + +QTimeZonePrivate::Data QMacTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const +{ + QTimeZonePrivate::Data tran; + const NSTimeInterval seconds = afterMSecsSinceEpoch / 1000.0; + NSDate *date = [NSDate dateWithTimeIntervalSince1970:seconds]; + const NSDate *next = [m_nstz nextDaylightSavingTimeTransitionAfterDate:date]; + const NSTimeInterval nextSecs = [next timeIntervalSince1970]; + tran.atMSecsSinceEpoch = nextSecs * 1000; + tran.offsetFromUtc = [m_nstz secondsFromGMTForDate: + [NSDate dateWithTimeIntervalSince1970:nextSecs]]; + tran.daylightTimeOffset = [m_nstz daylightSavingTimeOffsetForDate: + [NSDate dateWithTimeIntervalSince1970:nextSecs]]; + tran.standardTimeOffset = tran.offsetFromUtc - tran.daylightTimeOffset; + tran.abbreviation = QCFString::toQString([m_nstz abbreviationForDate:date]); + [next release]; + [date release]; + return tran; +} + +QTimeZonePrivate::Data QMacTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const +{ + // TODO No direct Mac API, so get all transitions since epoch and return the last one + // Probably slow, need to optimize + return transitions(0, beforeMSecsSinceEpoch - 1).last(); +} + +QByteArray QMacTimeZonePrivate::systemTimeZoneId() const +{ + // Reset the cached system tz then return the name + [NSTimeZone resetSystemTimeZone]; + return QCFString::toQString([[NSTimeZone systemTimeZone] name]).toUtf8(); +} + +QSet QMacTimeZonePrivate::availableTimeZoneIds() const +{ + NSEnumerator *enumerator = [[NSTimeZone knownTimeZoneNames] objectEnumerator]; + QByteArray tzid = QCFString::toQString([enumerator nextObject]).toUtf8(); + + QSet set; + while (!tzid.isEmpty()) { + set << tzid; + tzid = QCFString::toQString([enumerator nextObject]).toUtf8(); + } + + [enumerator release]; + return set; +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index 9120be49d1..bc28e627a9 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -61,6 +61,14 @@ #include #endif // QT_USE_ICU +#ifdef Q_OS_MAC +#ifdef __OBJC__ +@class NSTimeZone; +#else +class NSTimeZone; +#endif // __OBJC__ +#endif // Q_OS_MAC + QT_BEGIN_NAMESPACE class Q_CORE_EXPORT QTimeZonePrivate : public QSharedData @@ -306,6 +314,49 @@ private: }; #endif // Q_OS_UNIX +#ifdef Q_OS_MAC +class Q_AUTOTEST_EXPORT QMacTimeZonePrivate Q_DECL_FINAL : public QTimeZonePrivate +{ +public: + // Create default time zone + QMacTimeZonePrivate(); + // Create named time zone + QMacTimeZonePrivate(const QByteArray &olsenId); + QMacTimeZonePrivate(const QMacTimeZonePrivate &other); + ~QMacTimeZonePrivate(); + + QTimeZonePrivate *clone(); + + QString comment() const Q_DECL_OVERRIDE; + + 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 availableTimeZoneIds() const Q_DECL_OVERRIDE; + +private: + void init(const QByteArray &zoneId); + + NSTimeZone *m_nstz; +}; +#endif // Q_OS_MAC + QT_END_NAMESPACE #endif // QTIMEZONEPRIVATE_P_H diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index b06f870abd..2980d361db 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -116,6 +116,7 @@ SOURCES += \ !nacl:mac: { SOURCES += tools/qelapsedtimer_mac.cpp OBJECTIVE_SOURCES += tools/qlocale_mac.mm \ + tools/qtimezoneprivate_mac.mm \ tools/qstring_mac.mm } else:blackberry { -- cgit v1.2.3