Simplify fileTimeToQDateTime() by having it return a UTC time
This avoids so many complications. The prior code, using SystemTimeToTzSpecificLocalTime(), lead to unhelpful results when the QDateTime() implementation used MS-POSIX's defective mktime(). Although SystemTimeToTzSpecificLocalTime() is actually more correct, we were getting inconsistent results by mixing the two: and eliminating the use of mktime() turns out to be decidedly tricky. So, to avoid inconsistency, stick with a UTC time (which is what FILETIME is defined as). Change QFileInfo's methods to explicitly convert .toLocalTime() where appropriate and document that these methods do indeed return local time (as we conjecture has been taken for granted by callers). Also added a regression test for the reported case of this going wrong. A time-stamp from before Russia's (permanent, not DST) change of TZ could end up inconsistently handled between file-system meta-data and raw date-time APIs, due to cross-talk between different MS-Win time APIs. [ChangeLog][QtCore][QFileInfo] Made sure that all file lifecycle times are in local time. This was probably true before, but is now explicit. Task-number: QTBUG-48306 Change-Id: Ic0b99d25c4168f623d31967bc60665c0c4f38a14 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
80bbaf6fad
commit
4b4bd6ab98
@ -1295,7 +1295,7 @@ qint64 QFileInfo::size() const
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date and time when the file was created.
|
||||
Returns the date and local time when the file was created.
|
||||
|
||||
On most Unix systems, this function returns the time of the last
|
||||
status change. A status change occurs when the file is created,
|
||||
@ -1316,13 +1316,13 @@ QDateTime QFileInfo::created() const
|
||||
if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::CreationTime))
|
||||
if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::CreationTime))
|
||||
return QDateTime();
|
||||
return d->metaData.creationTime();
|
||||
return d->metaData.creationTime().toLocalTime();
|
||||
}
|
||||
return d->getFileTime(QAbstractFileEngine::CreationTime);
|
||||
return d->getFileTime(QAbstractFileEngine::CreationTime).toLocalTime();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date and time when the file was last modified.
|
||||
Returns the date and local time when the file was last modified.
|
||||
|
||||
\sa created(), lastRead()
|
||||
*/
|
||||
@ -1335,13 +1335,13 @@ QDateTime QFileInfo::lastModified() const
|
||||
if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ModificationTime))
|
||||
if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ModificationTime))
|
||||
return QDateTime();
|
||||
return d->metaData.modificationTime();
|
||||
return d->metaData.modificationTime().toLocalTime();
|
||||
}
|
||||
return d->getFileTime(QAbstractFileEngine::ModificationTime);
|
||||
return d->getFileTime(QAbstractFileEngine::ModificationTime).toLocalTime();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date and time when the file was last read (accessed).
|
||||
Returns the date and local time when the file was last read (accessed).
|
||||
|
||||
On platforms where this information is not available, returns the
|
||||
same as lastModified().
|
||||
@ -1357,9 +1357,9 @@ QDateTime QFileInfo::lastRead() const
|
||||
if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::AccessTime))
|
||||
if (!QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::AccessTime))
|
||||
return QDateTime();
|
||||
return d->metaData.accessTime();
|
||||
return d->metaData.accessTime().toLocalTime();
|
||||
}
|
||||
return d->getFileTime(QAbstractFileEngine::AccessTime);
|
||||
return d->getFileTime(QAbstractFileEngine::AccessTime).toLocalTime();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1368,15 +1368,11 @@ bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Per
|
||||
|
||||
static inline QDateTime fileTimeToQDateTime(const FILETIME *time)
|
||||
{
|
||||
QDateTime ret;
|
||||
|
||||
SYSTEMTIME sTime, lTime;
|
||||
SYSTEMTIME sTime;
|
||||
FileTimeToSystemTime(time, &sTime);
|
||||
SystemTimeToTzSpecificLocalTime(0, &sTime, &lTime);
|
||||
ret.setDate(QDate(lTime.wYear, lTime.wMonth, lTime.wDay));
|
||||
ret.setTime(QTime(lTime.wHour, lTime.wMinute, lTime.wSecond, lTime.wMilliseconds));
|
||||
|
||||
return ret;
|
||||
return QDateTime(QDate(sTime.wYear, sTime.wMonth, sTime.wDay),
|
||||
QTime(sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds),
|
||||
Qt::UTC);
|
||||
}
|
||||
|
||||
QDateTime QFileSystemMetaData::creationTime() const
|
||||
|
@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
class QFileSystemEngine;
|
||||
|
||||
class QFileSystemMetaData
|
||||
class Q_AUTOTEST_EXPORT QFileSystemMetaData
|
||||
{
|
||||
public:
|
||||
QFileSystemMetaData()
|
||||
|
@ -11,6 +11,7 @@ SUBDIRS=\
|
||||
largefile \
|
||||
qfileinfo \
|
||||
qfileselector \
|
||||
qfilesystemmetadata \
|
||||
qfilesystementry \
|
||||
qfilesystemwatcher \
|
||||
qiodevice \
|
||||
|
@ -0,0 +1,4 @@
|
||||
CONFIG += testcase parallel_test
|
||||
TARGET = tst_qfilesystemmetadata
|
||||
QT = core-private testlib
|
||||
SOURCES = tst_qfilesystemmetadata.cpp
|
@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: http://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL3$
|
||||
** 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 The Qt Company. For licensing terms
|
||||
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at http://www.qt.io/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 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or later 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 2.0 requirements will be
|
||||
** met: http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/private/qfilesystemmetadata_p.h>
|
||||
|
||||
class tst_QFileSystemMetaData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void timeSinceEpoch();
|
||||
};
|
||||
|
||||
#if defined(QT_BUILD_INTERNAL) && defined(QT_SHARED)
|
||||
#ifdef Q_OS_WIN
|
||||
static FILETIME epochToFileTime(long seconds)
|
||||
{
|
||||
const qint64 sec = 10000000;
|
||||
// FILETIME is time in 1e-7s units since 1601's start: epoch is 1970's
|
||||
// start, 369 years (of which 3*24 +69/4 = 89 were leap) later.
|
||||
const qint64 offset = qint64(365 * 369 + 89) * 24 * 3600;
|
||||
const qint64 convert = (offset + seconds) * sec;
|
||||
FILETIME parts;
|
||||
parts.dwHighDateTime = convert >> 32;
|
||||
parts.dwLowDateTime = convert & 0xffffffff;
|
||||
return parts;
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QFileSystemMetaData::timeSinceEpoch()
|
||||
{
|
||||
// Regression test for QTBUG-48306, used to fail for TZ=Russia/Moscow
|
||||
// Oct 22 2014 6:00 UTC; TZ=Russia/Moscow changed from +4 to +3 on Oct 26.
|
||||
const long afterEpochUtc = 1413957600L;
|
||||
QFileSystemMetaData meta;
|
||||
#ifdef Q_OS_WIN
|
||||
WIN32_FIND_DATA data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
/* data.ftLastAccessTime = data.ftLastWriteTime = */
|
||||
data.ftCreationTime = epochToFileTime(afterEpochUtc);
|
||||
meta.fillFromFindData(data);
|
||||
#else
|
||||
QT_STATBUF data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.st_ctime = afterEpochUtc;
|
||||
meta.fillFromStatBuf(data);
|
||||
#endif
|
||||
QCOMPARE(meta.creationTime().toUTC(),
|
||||
QDateTime::fromMSecsSinceEpoch(afterEpochUtc * qint64(1000), Qt::UTC));
|
||||
}
|
||||
#else // i.e. no Q_AUTOTEST_EXPORT
|
||||
void tst_QFileSystemMetaData::timeSinceEpoch()
|
||||
{
|
||||
QSKIP("QFileSystemMetaData methods aren't available to test");
|
||||
}
|
||||
#endif
|
||||
QTEST_MAIN(tst_QFileSystemMetaData)
|
||||
#include <tst_qfilesystemmetadata.moc>
|
Loading…
x
Reference in New Issue
Block a user