Allow set and get last modified times on files.

PiperOrigin-RevId: 791407269
This commit is contained in:
Francis Tsui
2025-08-05 16:08:30 -07:00
committed by Copybara-Service
parent 09e11ee122
commit 38827f2b4b
11 changed files with 144 additions and 6 deletions
+1
View File
@@ -352,6 +352,7 @@ cc_library(
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
],
)
+12
View File
@@ -13,6 +13,10 @@
// limitations under the License.
#include "internal/platform/file.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include "absl/time/time.h"
namespace nearby {
@@ -37,6 +41,10 @@ std::string InputFile::GetFilePath() const { return impl_->GetFilePath(); }
// Returns total size of this file in bytes.
std::int64_t InputFile::GetTotalSize() const { return impl_->GetTotalSize(); }
absl::Time InputFile::GetLastModifiedTime() const {
return impl_->GetLastModifiedTime();
}
ExceptionOr<size_t> InputFile::Skip(size_t offset) {
return impl_->Skip(offset);
}
@@ -81,4 +89,8 @@ Exception OutputFile::Close() { return impl_->Close(); }
// versa.
OutputStream& OutputFile::GetOutputStream() { return *impl_; }
void OutputFile::SetLastModifiedTime(absl::Time last_modified_time) {
impl_->SetLastModifiedTime(last_modified_time);
}
} // namespace nearby
+5
View File
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include "absl/time/time.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/input_file.h"
@@ -66,6 +67,8 @@ class InputFile final {
// versa.
InputStream& GetInputStream();
absl::Time GetLastModifiedTime() const;
private:
std::unique_ptr<api::InputFile> impl_;
};
@@ -98,6 +101,8 @@ class OutputFile final {
// versa.
OutputStream& GetOutputStream();
void SetLastModifiedTime(absl::Time last_modified_time);
private:
std::unique_ptr<api::OutputFile> impl_;
};
@@ -16,9 +16,9 @@
#define PLATFORM_API_INPUT_FILE_H_
#include <cstdint>
#include <string>
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "absl/time/time.h"
#include "internal/platform/input_stream.h"
namespace nearby {
@@ -30,6 +30,7 @@ class InputFile : public InputStream {
~InputFile() override = default;
virtual std::string GetFilePath() const = 0;
virtual std::int64_t GetTotalSize() const = 0;
virtual absl::Time GetLastModifiedTime() const = 0;
};
} // namespace api
@@ -15,6 +15,7 @@
#ifndef PLATFORM_API_OUTPUT_FILE_H_
#define PLATFORM_API_OUTPUT_FILE_H_
#include "absl/time/time.h"
#include "internal/platform/exception.h"
#include "internal/platform/output_stream.h"
@@ -25,6 +26,7 @@ namespace api {
class OutputFile : public OutputStream {
public:
~OutputFile() override = default;
virtual void SetLastModifiedTime(absl::Time last_modified_time) = 0;
// File flush is a no-op.
Exception Flush() override { return {Exception::kSuccess}; }
};
@@ -66,6 +66,7 @@ cc_library(
"//internal/platform/implementation:types",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)
@@ -21,6 +21,8 @@
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/platform/exception.h"
namespace nearby {
@@ -94,9 +96,13 @@ Exception IOFile::Write(const ByteArray& data) {
return {file_.good() ? Exception::kSuccess : Exception::kIo};
}
Exception IOFile::Flush() {
file_.flush();
return {file_.good() ? Exception::kSuccess : Exception::kIo};
absl::Time IOFile::GetLastModifiedTime() const {
// TODO(ftsui): Implement this method.
return absl::Now();
}
void IOFile::SetLastModifiedTime(absl::Time last_modified_time) {
// TODO(ftsui): Implement this method.
}
} // namespace shared
@@ -15,11 +15,14 @@
#ifndef PLATFORM_IMPL_SHARED_FILE_H_
#define PLATFORM_IMPL_SHARED_FILE_H_
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/input_file.h"
#include "internal/platform/implementation/output_file.h"
@@ -42,7 +45,9 @@ class IOFile final : public api::InputFile, public api::OutputFile {
Exception Close() override;
Exception Write(const ByteArray& data) override;
Exception Flush() override;
absl::Time GetLastModifiedTime() const override;
void SetLastModifiedTime(absl::Time last_modified_time) override;
private:
explicit IOFile(const absl::string_view file_path, size_t size);
@@ -16,6 +16,7 @@
#include <fileapi.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <limits>
@@ -24,6 +25,9 @@
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "absl/time/civil_time.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/windows/string_utils.h"
@@ -31,6 +35,57 @@
namespace nearby::windows {
namespace {
static const absl::Time kFiletimeEpoch =
absl::FromCivil(absl::CivilYear(1601), absl::UTCTimeZone());
constexpr absl::Duration kFiletimeUnit = absl::Nanoseconds(100);
const int64_t kFiletimeUnitsPerSecond = absl::Seconds(1) / kFiletimeUnit;
static const absl::Time kMinDosFileTime =
absl::FromCivil(absl::CivilYear(1980), absl::UTCTimeZone());
static const absl::Time kMaxDosFileTime =
absl::FromCivil(absl::CivilYear(2108), absl::UTCTimeZone()) - kFiletimeUnit;
absl::Duration DurationFromFiletime(const FILETIME& file_time) {
LONGLONG quadDateTime =
(static_cast<LONGLONG>(file_time.dwHighDateTime) << 32) |
static_cast<LONGLONG>(file_time.dwLowDateTime);
const absl::Duration subsecond =
kFiletimeUnit * (quadDateTime % kFiletimeUnitsPerSecond);
const absl::Duration seconds =
absl::Seconds(quadDateTime / kFiletimeUnitsPerSecond);
return seconds + subsecond;
}
absl::Time TimeFromFiletime(const FILETIME& file_time) {
return kFiletimeEpoch + DurationFromFiletime(file_time);
}
bool DurationToFiletime(const absl::Duration duration, FILETIME* file_time) {
absl::Duration remainder;
const int64_t filetime_value =
absl::IDivDuration(duration, kFiletimeUnit, &remainder);
if (remainder <= -kFiletimeUnit || remainder >= kFiletimeUnit) {
return false;
}
file_time->dwLowDateTime = static_cast<DWORD>(filetime_value);
file_time->dwHighDateTime = static_cast<DWORD>(filetime_value >> 32);
return true;
}
void TimeToFiletime(absl::Time time, FILETIME* file_time) {
// Clamp to the range that FileTimeToDosDateTime supports. Explorer's date
// display and built-in zip feature can only handle this range.
time = std::clamp(time, kMinDosFileTime, kMaxDosFileTime);
if (!DurationToFiletime(time - kFiletimeEpoch, file_time)) {
DurationToFiletime(kMaxDosFileTime - kFiletimeEpoch, file_time);
}
}
} // namespace
// InputFile
std::unique_ptr<IOFile> IOFile::CreateInputFile(absl::string_view file_path,
size_t size) {
@@ -128,4 +183,34 @@ Exception IOFile::Write(const ByteArray& data) {
return {Exception::kSuccess};
}
absl::Time IOFile::GetLastModifiedTime() const {
if (file_ == INVALID_HANDLE_VALUE) {
LOG(ERROR) << "Failed to get file modified time for: " << path_;
return absl::Now();
}
FILETIME last_write_time;
if (::GetFileTime(file_, /*lpCreationTime=*/nullptr,
/*lpLastAccessTime=*/nullptr, &last_write_time) == 0) {
LOG(ERROR) << "Failed to get file last write time: " << path_
<< " with error: " << ::GetLastError();
return absl::Now();
}
return TimeFromFiletime(last_write_time);
}
void IOFile::SetLastModifiedTime(absl::Time last_modified_time) {
if (file_ == INVALID_HANDLE_VALUE) {
LOG(ERROR) << "Failed to set file modified time for: " << path_;
return;
}
FILETIME last_write_time;
TimeToFiletime(last_modified_time, &last_write_time);
// Set both creation time and last write time.
if (::SetFileTime(file_, /*lpCreationTime=*/&last_write_time,
/*lpLastAccessTime=*/nullptr, &last_write_time) == 0) {
LOG(ERROR) << "Failed to set file last write time: " << path_
<< " with error: " << ::GetLastError();
}
}
} // namespace nearby::windows
@@ -23,6 +23,7 @@
#include <string>
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/input_file.h"
@@ -49,6 +50,8 @@ class IOFile final : public api::InputFile, public api::OutputFile {
Exception Close() override;
Exception Write(const ByteArray& data) override;
absl::Time GetLastModifiedTime() const override;
void SetLastModifiedTime(absl::Time last_modified_time) override;
private:
explicit IOFile(absl::string_view file_path, size_t size);
@@ -23,6 +23,7 @@
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/logging.h"
@@ -187,5 +188,21 @@ TEST(IOFileTest, OutputFileWrite) {
::DeleteFileA(temp_file.data());
}
TEST(IOFileTest, GetSetModifiedTime) {
std::string temp_file = GetTempFileName("GetSetModifiedTime");
std::unique_ptr<IOFile> output_file = IOFile::CreateOutputFile(temp_file);
ASSERT_NE(output_file, nullptr);
output_file->SetLastModifiedTime(absl::FromUnixSeconds(1234567890));
output_file->Close();
std::unique_ptr<IOFile> input_file =
IOFile::CreateInputFile(temp_file, /*size=*/0);
EXPECT_EQ(input_file->GetLastModifiedTime(),
absl::FromUnixSeconds(1234567890));
::DeleteFileA(temp_file.data());
}
} // namespace
} // namespace nearby::windows