diff --git a/src/internal/dateutil.cpp b/src/internal/dateutil.cpp index badab2be..21b27be4 100644 --- a/src/internal/dateutil.cpp +++ b/src/internal/dateutil.cpp @@ -26,15 +26,24 @@ using FileTimeDuration = std::chrono::duration< int64_t, FileTimeTickRate >; // Seconds between 01/01/1601 (NT epoch) and 01/01/1970 (Unix epoch): constexpr std::chrono::seconds nt_to_unix_epoch{ -11644473600 }; -#ifndef _WIN32 +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) +// Seconds between 01/01/1970 (Unix epoch) and 01/01/2174 (libstdc++'s file_clock epoch). +constexpr std::chrono::seconds libstdcpp_file_clock_epoch{ -6437664000 }; +#endif +#ifndef _WIN32 auto FILETIME_to_file_time_type( FILETIME fileTime ) -> fs::file_time_type { const FileTimeDuration fileTimeDuration{ - ( static_cast< int64_t >( fileTime.dwHighDateTime ) << 32 ) + fileTime.dwLowDateTime + ( static_cast< std::uint64_t >( fileTime.dwHighDateTime ) << 32ull ) + fileTime.dwLowDateTime }; - const auto unixEpoch = fileTimeDuration + nt_to_unix_epoch; - return fs::file_time_type{ std::chrono::duration_cast< std::chrono::system_clock::duration >( unixEpoch ) }; + const auto unixFileTime = fileTimeDuration + nt_to_unix_epoch; + const auto systemFileTime = std::chrono::duration_cast< fs::file_time_type::clock::duration >( unixFileTime ); +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) + return fs::file_time_type{ systemFileTime + libstdcpp_file_clock_epoch }; +#else + return fs::file_time_type{ systemFileTime }; +#endif } auto time_to_FILETIME( std::time_t timeValue ) -> FILETIME { diff --git a/tests/src/test_bitinputarchive.cpp b/tests/src/test_bitinputarchive.cpp index 0e146484..c611cbcf 100644 --- a/tests/src/test_bitinputarchive.cpp +++ b/tests/src/test_bitinputarchive.cpp @@ -1032,7 +1032,13 @@ TEMPLATE_TEST_CASE( "BitInputArchive: Extracting an archive to the filesystem sh REQUIRE( expectedModifiedTime.dwHighDateTime != 0 ); #else namespace chrono = std::chrono; - const auto expectedModifiedTime = chrono::duration_cast< chrono::seconds >( item.lastWriteTime().time_since_epoch() ); + const auto expectedModifiedTime = chrono::duration_cast< chrono::seconds >( +#ifdef __GLIBCXX__ + item.lastWriteTime().time_since_epoch() - std::chrono::seconds{6437664000} +#else + item.lastWriteTime().time_since_epoch() +#endif + ); #endif TempTestDirectory testOutDir{ "test_bitinputarchive" }; @@ -1051,7 +1057,12 @@ TEMPLATE_TEST_CASE( "BitInputArchive: Extracting an archive to the filesystem sh REQUIRE( CompareFileTime( &accessTime, &expectedAccessTime ) == 0 ); REQUIRE( CompareFileTime( &modifiedTime, &expectedModifiedTime ) == 0 ); #else - const auto modifiedTime = chrono::duration_cast< chrono::seconds >( fs::last_write_time( expectedFile ).time_since_epoch() ); + auto modifiedTime = chrono::duration_cast< chrono::seconds >( fs::last_write_time( expectedFile ).time_since_epoch() ); +#ifdef __GLIBCXX__ + if ( modifiedTime.count() > 0 ) { + modifiedTime -= std::chrono::seconds{6437664000} - std::chrono::seconds{1}; + } +#endif // Note: Using count() since Catch2 cannot print std::chrono::duration objects. INFO( "System clock's now: " << static_cast< std::uint64_t >( chrono::duration_cast< chrono::seconds >( chrono::system_clock::now().time_since_epoch() ).count() ) ) INFO( "File clock's now: " << static_cast< std::uint64_t >( chrono::duration_cast< chrono::seconds >( fs::file_time_type::clock::now().time_since_epoch() ).count() ) ) diff --git a/tests/src/test_dateutil.cpp b/tests/src/test_dateutil.cpp index 93f62aec..d168b581 100644 --- a/tests/src/test_dateutil.cpp +++ b/tests/src/test_dateutil.cpp @@ -44,14 +44,35 @@ struct DateConversionTest { }; #ifndef _WIN32 + +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM) && defined( __GLIBCXX__ ) +constexpr std::chrono::seconds libstdcpp_file_clock_epoch{ 6437664000 }; +#endif + template< class Clock, class Duration = typename Clock::duration > -auto as_timestamp( const std::chrono::time_point< Clock, Duration > timePoint ) -> std::uint64_t { +auto as_unix_timestamp( const std::chrono::time_point< Clock, Duration > timePoint ) -> std::uint64_t { const auto asSeconds = std::chrono::duration_cast< std::chrono::seconds >( timePoint.time_since_epoch() ); return static_cast< std::uint64_t >( asSeconds.count() ); } + +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM) && defined( __GLIBCXX__ ) +template<> +auto as_unix_timestamp( const fs::file_time_type timePoint ) -> std::uint64_t { + const auto sinceEpoch = timePoint.time_since_epoch(); + const auto asSeconds = std::chrono::duration_cast< std::chrono::seconds >( sinceEpoch ); + const auto sinceUnixEpoch = libstdcpp_file_clock_epoch + asSeconds; + + auto nano = std::chrono::duration_cast< std::chrono::nanoseconds >(sinceEpoch - asSeconds); + if ( nano < fs::file_time_type::duration::zero() ) { + return static_cast< std::uint64_t >( ( sinceUnixEpoch - std::chrono::seconds{ 1 } ).count() ); + } + + return static_cast< std::uint64_t >( sinceUnixEpoch.count() ); +} +#endif #endif TEST_CASE( "fsutil: Date conversions", "[fsutil][date functions]" ) { @@ -66,17 +87,16 @@ TEST_CASE( "fsutil: Date conversions", "[fsutil][date functions]" ) { DYNAMIC_SECTION( "Date: " << testDate.name ) { #ifndef _WIN32 SECTION( "From std::time_t to FILETIME" ) { - auto output = time_to_FILETIME( testDate.dateTime ); - REQUIRE( output.dwHighDateTime == testDate.fileTime.dwHighDateTime ); - REQUIRE( output.dwLowDateTime == testDate.fileTime.dwLowDateTime ); + auto result = time_to_FILETIME( testDate.dateTime ); + REQUIRE( result.dwHighDateTime == testDate.fileTime.dwHighDateTime ); + REQUIRE( result.dwLowDateTime == testDate.fileTime.dwLowDateTime ); } #endif #ifndef _WIN32 SECTION( "From FILETIME to std::filesystem::file_time_type" ) { auto result = FILETIME_to_file_time_type( testDate.fileTime ); - auto output = duration_cast< seconds >( result.time_since_epoch() ).count(); - REQUIRE( output == testDate.dateTime ); + REQUIRE( as_unix_timestamp( result ) == testDate.dateTime ); } #endif @@ -92,7 +112,7 @@ TEST_CASE( "fsutil: Date conversions", "[fsutil][date functions]" ) { TEST_CASE( "fsutil: Date conversion of current time should preserve information up to seconds", "[fsutil][date functions]" ) { const auto currentTime = std::chrono::system_clock::now(); - const auto unixTimestamp = as_timestamp( currentTime ); + const auto unixTimestamp = as_unix_timestamp( currentTime ); INFO( "Current time: " << unixTimestamp ) // Converting the current time to FILETIME @@ -101,12 +121,12 @@ TEST_CASE( "fsutil: Date conversion of current time should preserve information SECTION( "Converting current FILETIME to a system_clock's time_point" ) { const auto asSystemTimePoint = FILETIME_to_time_type( asFileTime ); - REQUIRE( unixTimestamp == as_timestamp( asSystemTimePoint ) ); + REQUIRE( unixTimestamp == as_unix_timestamp( asSystemTimePoint ) ); } SECTION( "Converting current FILETIME to a file_clock's time_point" ) { const auto asFileTimePoint = FILETIME_to_file_time_type( asFileTime ); - REQUIRE( unixTimestamp == as_timestamp( asFileTimePoint ) ); + REQUIRE( unixTimestamp == as_unix_timestamp( asFileTimePoint ) ); } } @@ -126,11 +146,11 @@ TEMPLATE_TEST_CASE( "fsutil: Date conversion of last write time", "[fsutil][date INFO( "Last write time FILETIME: {" << lastWriteTime.dwHighDateTime << ", " << lastWriteTime.dwLowDateTime << "}") const auto result = FILETIME_to_file_time_type( lastWriteTime ); - const auto result_as_timestamp = as_timestamp( result ); + const auto result_as_timestamp = as_unix_timestamp( result ); INFO( "Last write file time_point: " << result_as_timestamp ) const auto result2 = item.lastWriteTime(); - const auto result2_as_timestamp = as_timestamp( result2 ); + const auto result2_as_timestamp = as_unix_timestamp( result2 ); INFO( "Last write system time_point: " << result2_as_timestamp ) REQUIRE( result_as_timestamp == result2_as_timestamp ); }