Skip to content

Commit 9404caa

Browse files
authored
Adopt utimensat for setting file modification dates (#1324)
1 parent e71181d commit 9404caa

File tree

2 files changed

+23
-9
lines changed

2 files changed

+23
-9
lines changed

Sources/FoundationEssentials/FileManager/FileManager+Files.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -965,15 +965,15 @@ extension _FileManagerImpl {
965965

966966
if let date = attributes[.modificationDate] as? Date {
967967
let (isecs, fsecs) = modf(date.timeIntervalSince1970)
968-
if let tv_sec = time_t(exactly: isecs),
969-
let tv_usec = suseconds_t(exactly: round(fsecs * 1000000.0)) {
970-
var timevals = (timeval(), timeval())
971-
timevals.0.tv_sec = tv_sec
972-
timevals.0.tv_usec = tv_usec
973-
timevals.1 = timevals.0
974-
try withUnsafePointer(to: timevals) {
975-
try $0.withMemoryRebound(to: timeval.self, capacity: 2) {
976-
if utimes(fileSystemRepresentation, $0) != 0 {
968+
if let tv_sec = Int(exactly: isecs),
969+
let tv_nsec = Int(exactly: round(fsecs * 1000000000.0)) {
970+
var timespecs = (timespec(), timespec())
971+
timespecs.0.tv_sec = tv_sec
972+
timespecs.0.tv_nsec = tv_nsec
973+
timespecs.1 = timespecs.0
974+
try withUnsafePointer(to: timespecs) {
975+
try $0.withMemoryRebound(to: timespec.self, capacity: 2) {
976+
if utimensat(AT_FDCWD, fileSystemRepresentation, $0, 0) != 0 {
977977
throw CocoaError.errorWithFilePath(path, errno: errno, reading: false)
978978
}
979979
}

Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,20 @@ final class FileManagerTests : XCTestCase {
793793
}
794794
}
795795

796+
func testRoundtripModificationDate() throws {
797+
try FileManagerPlayground {
798+
"foo"
799+
}.test {
800+
// Precision of modification dates is dependent not only on the platform, but on the file system used
801+
// Ensure that roundtripping supports at least millisecond-level precision, but some file systems may support more up to nanosecond precision
802+
let date = Date(timeIntervalSince1970: 10.003)
803+
try $0.setAttributes([.modificationDate : date], ofItemAtPath: "foo")
804+
let readValue = try XCTUnwrap($0.attributesOfItem(atPath: "foo")[.modificationDate], "No value provided for file modification date")
805+
let readDate = try XCTUnwrap(readValue as? Date, "File modification date was not a date (found type \(type(of: readValue)))")
806+
XCTAssertEqual(readDate.timeIntervalSince1970, date.timeIntervalSince1970, accuracy: 0.0005, "File modification date (\(readDate.timeIntervalSince1970)) does not match expected modification date (\(date.timeIntervalSince1970))")
807+
}
808+
}
809+
796810
func testImplicitlyConvertibleFileAttributes() throws {
797811
try FileManagerPlayground {
798812
File("foo", attributes: [.posixPermissions : UInt16(0o644)])

0 commit comments

Comments
 (0)