From f4bfa86aeefee010ebab14a51d485bd058d0a1fa Mon Sep 17 00:00:00 2001 From: Matej Kenda Date: Mon, 29 Jan 2024 19:06:06 +0100 Subject: [PATCH] enh(File): Linux, macOS: microsecond precision for file times (create and modification time). --- Foundation/include/Poco/Timestamp.h | 4 +-- Foundation/src/File_UNIX.cpp | 45 +++++++++++++++++++++------ Foundation/testsuite/src/GlobTest.cpp | 6 ++-- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Foundation/include/Poco/Timestamp.h b/Foundation/include/Poco/Timestamp.h index 089ae05b41..cb7ae662db 100644 --- a/Foundation/include/Poco/Timestamp.h +++ b/Foundation/include/Poco/Timestamp.h @@ -135,7 +135,7 @@ class Foundation_API Timestamp /// (100 nanosecond intervals since midnight, /// October 15, 1582). - static TimeDiff resolution(); + static constexpr TimeDiff resolution(); /// Returns the resolution in units per second. /// Since the timestamp has microsecond resolution, /// the returned value is always 1000000. @@ -254,7 +254,7 @@ inline bool Timestamp::isElapsed(Timestamp::TimeDiff interval) const } -inline Timestamp::TimeDiff Timestamp::resolution() +inline constexpr Timestamp::TimeDiff Timestamp::resolution() { return 1000000; } diff --git a/Foundation/src/File_UNIX.cpp b/Foundation/src/File_UNIX.cpp index b4fd9b6da0..b06ff133f8 100644 --- a/Foundation/src/File_UNIX.cpp +++ b/Foundation/src/File_UNIX.cpp @@ -212,15 +212,24 @@ Timestamp FileImpl::createdImpl() const { poco_assert (!_path.empty()); -#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(_DARWIN_FEATURE_64_BIT_INODE)) + using TV = Timestamp::TimeVal; + + // Nanosecond to timestamp resolution factor + static constexpr TV nsk = 1'000'000'000ll / Timestamp::resolution(); + struct stat st; - if (stat(_path.c_str(), &st) == 0) - return Timestamp::fromEpochTime(st.st_birthtime); + if (::stat(_path.c_str(), &st) == 0) + { +#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(_DARWIN_FEATURE_64_BIT_INODE)) + const TV tv = static_cast(st.st_birthtimespec.tv_sec) * Timestamp::resolution() + st.st_birthtimespec.tv_nsec/nsk; + return Timestamp(tv); +#elif POCO_OS == POCO_OS_LINUX + const TV tv = static_cast(st.st_ctim.tv_sec) * Timestamp::resolution() + st.st_ctim.tv_nsec/nsk; + return Timestamp(tv); #else - struct stat st; - if (stat(_path.c_str(), &st) == 0) return Timestamp::fromEpochTime(st.st_ctime); #endif + } else handleLastErrorImpl(_path); return 0; @@ -231,9 +240,24 @@ Timestamp FileImpl::getLastModifiedImpl() const { poco_assert (!_path.empty()); + using TV = Timestamp::TimeVal; + + // Nanosecond to timestamp resolution factor + static constexpr TV nsk = 1'000'000'000ll / Timestamp::resolution(); + struct stat st; - if (stat(_path.c_str(), &st) == 0) + if (::stat(_path.c_str(), &st) == 0) + { +#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(_DARWIN_FEATURE_64_BIT_INODE)) + const TV tv = static_cast(st.st_mtimespec.tv_sec) * Timestamp::resolution() + st.st_mtimespec.tv_nsec/nsk; + return Timestamp(tv); +#elif POCO_OS == POCO_OS_LINUX + const TV tv = static_cast(st.st_mtim.tv_sec) * Timestamp::resolution() + st.st_mtim.tv_nsec/nsk; + return Timestamp(tv); +#else return Timestamp::fromEpochTime(st.st_mtime); +#endif + } else handleLastErrorImpl(_path); return 0; @@ -244,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) + const ::time_t s = ts.epochTime(); + const ::suseconds_t us = ts.epochMicroseconds() % 1'000'000; + const ::timeval times[2] = { {s, us}, {s, us} }; + + if (::utimes(_path.c_str(), times) != 0) handleLastErrorImpl(_path); } diff --git a/Foundation/testsuite/src/GlobTest.cpp b/Foundation/testsuite/src/GlobTest.cpp index 368d2ab890..c4d2428c77 100644 --- a/Foundation/testsuite/src/GlobTest.cpp +++ b/Foundation/testsuite/src/GlobTest.cpp @@ -443,7 +443,7 @@ void GlobTest::testGlob() files.clear(); Glob::glob("globtest/*/", files); translatePaths(files); - assertTrue (files.size() == 3); + assertEqual (3, files.size()); assertTrue (files.find("globtest/include/") != files.end()); assertTrue (files.find("globtest/src/") != files.end()); assertTrue (files.find("globtest/testsuite/") != files.end()); @@ -451,7 +451,7 @@ void GlobTest::testGlob() files.clear(); Glob::glob("globtest/testsuite/src/*", "globtest/testsuite/", files); translatePaths(files); - assertTrue (files.size() == 3); + assertEqual (3, files.size()); assertTrue (files.find("globtest/testsuite/src/test.h") != files.end()); assertTrue (files.find("globtest/testsuite/src/test.c") != files.end()); assertTrue (files.find("globtest/testsuite/src/main.c") != files.end()); @@ -460,7 +460,7 @@ void GlobTest::testGlob() files.clear(); Glob::glob("globtest/../*/testsuite/*/", files); translatePaths(files); - assertTrue (files.size() == 1); + assertEqual (1, files.size()); File dir("globtest"); dir.remove(true);