Skip to content

Commit

Permalink
Fix conversion of FILETIME to file_time_type when using libstdc++
Browse files Browse the repository at this point in the history
  • Loading branch information
rikyoz committed Jun 25, 2024
1 parent ca4624c commit 8d75f7b
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 17 deletions.
17 changes: 13 additions & 4 deletions src/internal/dateutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 13 additions & 2 deletions tests/src/test_bitinputarchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" };
Expand All @@ -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() ) )
Expand Down
42 changes: 31 additions & 11 deletions tests/src/test_dateutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]" ) {
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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 ) );
}
}

Expand All @@ -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 );
}
Expand Down

0 comments on commit 8d75f7b

Please sign in to comment.