diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index b2666ccbc..51a9bd750 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -965,15 +965,15 @@ extension _FileManagerImpl { if let date = attributes[.modificationDate] as? Date { let (isecs, fsecs) = modf(date.timeIntervalSince1970) - if let tv_sec = time_t(exactly: isecs), - let tv_usec = suseconds_t(exactly: round(fsecs * 1000000.0)) { - var timevals = (timeval(), timeval()) - timevals.0.tv_sec = tv_sec - timevals.0.tv_usec = tv_usec - timevals.1 = timevals.0 - try withUnsafePointer(to: timevals) { - try $0.withMemoryRebound(to: timeval.self, capacity: 2) { - if utimes(fileSystemRepresentation, $0) != 0 { + if let tv_sec = Int(exactly: isecs), + let tv_nsec = Int(exactly: round(fsecs * 1000000000.0)) { + var timespecs = (timespec(), timespec()) + timespecs.0.tv_sec = tv_sec + timespecs.0.tv_nsec = tv_nsec + timespecs.1 = timespecs.0 + try withUnsafePointer(to: timespecs) { + try $0.withMemoryRebound(to: timespec.self, capacity: 2) { + if utimensat(AT_FDCWD, fileSystemRepresentation, $0, 0) != 0 { throw CocoaError.errorWithFilePath(path, errno: errno, reading: false) } } diff --git a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift index 833566543..29c94c5fa 100644 --- a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift +++ b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift @@ -793,6 +793,20 @@ final class FileManagerTests : XCTestCase { } } + func testRoundtripModificationDate() throws { + try FileManagerPlayground { + "foo" + }.test { + // Precision of modification dates is dependent not only on the platform, but on the file system used + // Ensure that roundtripping supports at least millisecond-level precision, but some file systems may support more up to nanosecond precision + let date = Date(timeIntervalSince1970: 10.003) + try $0.setAttributes([.modificationDate : date], ofItemAtPath: "foo") + let readValue = try XCTUnwrap($0.attributesOfItem(atPath: "foo")[.modificationDate], "No value provided for file modification date") + let readDate = try XCTUnwrap(readValue as? Date, "File modification date was not a date (found type \(type(of: readValue)))") + XCTAssertEqual(readDate.timeIntervalSince1970, date.timeIntervalSince1970, accuracy: 0.0005, "File modification date (\(readDate.timeIntervalSince1970)) does not match expected modification date (\(date.timeIntervalSince1970))") + } + } + func testImplicitlyConvertibleFileAttributes() throws { try FileManagerPlayground { File("foo", attributes: [.posixPermissions : UInt16(0o644)])