Preserve modification date when updating access time

This commit is contained in:
Minh Vu 2026-07-24 19:29:34 +02:00
parent cbc160a592
commit 7e77e5c964
2 changed files with 22 additions and 14 deletions

View File

@ -8,21 +8,21 @@ extension URL {
}
func updateAccessDate(_ accessDate: Date = Date()) throws {
let attrs = try resourceValues(forKeys: [.contentAccessDateKey])
let modificationDate = attrs.contentAccessDate!
let times = [accessDate.asTimeval(), modificationDate.asTimeval()]
let ret = utimes(path, times)
let times = [accessDate.asTimespec(), timespec(tv_sec: 0, tv_nsec: Int(UTIME_OMIT))]
let ret = utimensat(AT_FDCWD, path, times, 0)
if ret != 0 {
let details = Errno(rawValue: CInt(errno))
throw RuntimeError.FailedToUpdateAccessDate("utimes(2) failed: \(details)")
throw RuntimeError.FailedToUpdateAccessDate("utimensat(2) failed: \(details)")
}
}
}
extension Date {
func asTimeval() -> timeval {
timeval(tv_sec: Int(timeIntervalSince1970), tv_usec: 0)
func asTimespec() -> timespec {
let seconds = floor(timeIntervalSince1970)
let nanoseconds = (timeIntervalSince1970 - seconds) * 1_000_000_000
return timespec(tv_sec: Int(seconds), tv_nsec: Int(nanoseconds))
}
}

View File

@ -2,21 +2,29 @@ import XCTest
@testable import tart
final class URLAccessDateTests: XCTestCase {
func testGetAndSetAccessTime() throws {
func testUpdateAccessDatePreservesModificationDate() throws {
// Create a temporary file
let tmpDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
var tmpFile = tmpDir.appendingPathComponent(UUID().uuidString)
FileManager.default.createFile(atPath: tmpFile.path, contents: nil)
defer { try? FileManager.default.removeItem(at: tmpFile) }
// Ensure it's access date is different than our desired access date
let arbitraryDate = Date.init(year: 2008, month: 09, day: 28, hour: 23, minute: 15)
XCTAssertNotEqual(arbitraryDate, try tmpFile.accessDate())
// Ensure its access date is different from our desired access date
let accessDate = Date.init(year: 2008, month: 09, day: 28, hour: 23, minute: 15)
let modificationDate = Date(timeIntervalSince1970: 1_577_836_800.125)
try FileManager.default.setAttributes([.modificationDate: modificationDate], ofItemAtPath: tmpFile.path)
XCTAssertNotEqual(accessDate, try tmpFile.accessDate())
// Set our desired access date for a file
try tmpFile.updateAccessDate(arbitraryDate)
try tmpFile.updateAccessDate(accessDate)
// Ensure the access date has changed to our value
tmpFile.removeCachedResourceValue(forKey: .contentAccessDateKey)
XCTAssertEqual(arbitraryDate, try tmpFile.accessDate())
XCTAssertEqual(accessDate, try tmpFile.accessDate())
// Ensure the modification date has not changed
tmpFile.removeCachedResourceValue(forKey: .contentModificationDateKey)
let attrs = try tmpFile.resourceValues(forKeys: [.contentModificationDateKey])
XCTAssertEqual(modificationDate, try XCTUnwrap(attrs.contentModificationDate))
}
}