Search code examples
swiftmacosnsdatensdata

Convert DATA to Date


I am trying to get last used date of a file using NSFileManager, i am getting the date in NSData format but i am not sure how to convert Data to Date.

I am getting below values from NSFileManager

key : "com.apple.lastuseddate#PS"
value : <b9b6c35e 00000000 abd73225 00000000>

Please let me know on how to convert above Data value to Date.

I used below function to convert data to date but i am getting completely wrong values.

func dataToDate(data:Data) -> Date{
    let components = NSDateComponents()
    let bytes = [uint8](data)
    components.year   = Int(bytes[0] | bytes[1] << 8)
    components.month  = Int(bytes[2])
    components.day    = Int(bytes[3])
    components.hour   = Int(bytes[4])
    components.minute = Int(bytes[5])
    components.second = Int(bytes[6])

    let calendar = NSCalendar.current
    return calendar.date(from: components as DateComponents)!
}

Edit: @Martin R, below is the code which i used to get the data.

var attributes:[FileAttributeKey : Any]?
do{
    attributes = try FileManager.default.attributesOfItem(atPath: url.path)
}catch{
    print("Issue getting attributes of file")
}

if let extendedAttr = attributes![FileAttributeKey(rawValue: "NSFileExtendedAttributes")] as? [String : Any]{
    let data = extendedAttr["com.apple.lastuseddate#PS"] as? Data
}

Solution

  • The necessary information can be found in Data to different types ? in the Apple Developer Forum.

    First note that it is unsafe to rely on undocumented extended attributes. A better way to get the same result is to retrieve the NSMetadataItemLastUsedDateKey from an NSMetadataItem:

    if let date = NSMetadataItem(url: url)?.value(forAttribute: NSMetadataItemLastUsedDateKey) as? Date {
        print(date)
    }
    

    But to answer your actual question: That extended attribute holds a UNIX struct timespec (compare <time.h>) value. That is the type used for st_atimespec and other members of struct stat (which in turn is the type used with fstat() and similar system calls).

    You have to copy the data into a timespec value, compute the seconds from the tv_sec and tv_nsec members, and then create a Date from the seconds since the Unix epoch.

    func dataToDate(data: Data) -> Date {
        var ts = timespec()
        precondition(data.count >= MemoryLayout.size(ofValue: ts))
        _ = withUnsafeMutableBytes(of: &ts, { lastuseddata.copyBytes(to: $0)} )
        let seconds = TimeInterval(ts.tv_sec) + TimeInterval(ts.tv_nsec)/TimeInterval(NSEC_PER_SEC)
        return Date(timeIntervalSince1970: seconds)
    }
    

    Example (your data):

    let lastuseddata = Data([0xb9, 0xb6, 0xc3, 0x5e, 0x00, 0x00, 0x00, 0x00,
                             0xab, 0xd7, 0x32, 0x25, 0x00, 0x00, 0x00, 0x00])
    
    print(dataToDate(data: lastuseddata))
    // 2020-05-19 10:36:41 +0000