Improves timestamp resolution on Unix systems

Adds microsecond precision when possible on Linux,
BSD and Apple systems.

Uses platform specific `struct stat`, and `utimes()`,
instead of `utime()`.
This commit is contained in:
Benoît Bleuzé 2016-04-15 17:20:23 +02:00
parent d11b69ca00
commit 76254ca055

View File

@ -20,6 +20,7 @@
#include <algorithm>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#if defined(POCO_OS_FAMILY_BSD)
#include <sys/param.h>
#include <sys/mount.h>
@ -30,9 +31,15 @@
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <utime.h>
#include <cstring>
namespace{
// Convert timespec structures (seconds and remaining nano secs) to TimeVal (microseconds)
Poco::Timestamp::TimeVal timespec2Microsecs(const struct timespec &ts) {
return ts.tv_sec * 1000000L + ts.tv_nsec / 1000L;
}
} // namespace
namespace Poco {
@ -201,15 +208,29 @@ bool FileImpl::isHiddenImpl() const
Timestamp FileImpl::createdImpl() const
{
poco_assert (!_path.empty());
// first, platforms with birthtime attributes
#if defined(__APPLE__) && defined(st_birthtime) && !defined(POCO_NO_STAT64) // st_birthtime is available only on 10.5
// a macro st_birthtime makes sure there is a st_birthtimespec (nano sec precision)
struct stat64 st;
if (stat64(_path.c_str(), &st) == 0)
return Timestamp::fromEpochTime(st.st_birthtime);
return Timestamp(timespec2Microsecs(st.st_birthtimespec);
#elif defined(__FreeBSD__) && defined(st_birthtime)
// a macro st_birthtime makes sure there is a st_birthtimespec (nano sec precision)
struct stat st;
if (stat(_path.c_str(), &st) == 0)
return Timestamp(timespec2Microsecs(st.st_birthtimespec));
#elif defined(__FreeBSD__)
struct stat st;
if (stat(_path.c_str(), &st) == 0)
return Timestamp::fromEpochTime(st.st_birthtime);
// then platforms with POSIX 2008-09 compatibility (nanosec precision)
// (linux 2.6 and later)
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) \
|| (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700)
struct stat st;
if (stat(_path.c_str(), &st) == 0)
return Timestamp(timespec2Microsecs(st.st_ctim));
// finally try just stat with status change with precision to the second.
#else
struct stat st;
if (stat(_path.c_str(), &st) == 0)
@ -226,8 +247,17 @@ Timestamp FileImpl::getLastModifiedImpl() const
poco_assert (!_path.empty());
struct stat st;
if (stat(_path.c_str(), &st) == 0)
if (stat(_path.c_str(), &st) == 0) {
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) \
|| (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) \
|| defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
return Timestamp(timespec2Microsecs(st.st_mtim));
#elif defined(__APPLE__)
return Timestamp(timespec2Microsecs(st.st_mtimespec));
#else
return Timestamp::fromEpochTime(st.st_mtime);
#endif
}
else
handleLastErrorImpl(_path);
return 0;
@ -238,10 +268,11 @@ void FileImpl::setLastModifiedImpl(const Timestamp& ts)
{
poco_assert (!_path.empty());
struct utimbuf tb;
tb.actime = ts.epochTime();
tb.modtime = ts.epochTime();
if (utime(_path.c_str(), &tb) != 0)
struct timeval tb[2];
tb[0].tv_sec = ts.epochMicroseconds() / 1000000;
tb[0].tv_usec = ts.epochMicroseconds() % 1000000;
tb[1] = tb[0];
if (utimes(_path.c_str(), tb) != 0)
handleLastErrorImpl(_path);
}