26

I'm trying to use Firebase timestamps in a Swift app. I'd like to store them in my Firebase, and use them as native NSDate objects in my app.

The docs say they are unix epoch time, so I've tried:

NSDate(timeIntervalSince1970:FirebaseServerValue.timestamp)

with no luck.

This:

FirebaseServerValue.timestamp

returns

0x00000001199298a0

according to the debugger. What is the best way to pass these timestamps around?

Fook
  • 5,320
  • 7
  • 35
  • 57

11 Answers11

40

ServerValue.timestamp() works a little differently than setting normal data in Firebase. It does not actually provide a timestamp. Instead, it provides a value which tells the Firebase server to fill in that node with the time. By using this, your app's timestamps will all come from one source, Firebase, instead of whatever the user's device happens to say.

When you get the value back (from a observer), you'll get the time as milliseconds since the epoch. You'll need to convert it to seconds to create an NSDate. Here's a snippet of code:

let ref = Firebase(url: "<FIREBASE HERE>")

// Tell the server to set the current timestamp at this location.
ref.setValue(ServerValue.timestamp()) 

// Read the value at the given location. It will now have the time.
ref.observeEventType(.Value, withBlock: { 
    snap in
    if let t = snap.value as? NSTimeInterval {
        // Cast the value to an NSTimeInterval
        // and divide by 1000 to get seconds.
        println(NSDate(timeIntervalSince1970: t/1000))
    }
})

You may find that you get two events raised with very close timestamps. This is because the SDK will take a best "guess" at the timestamp before it hears back from Firebase. Once it hears the actual value from Firebase, it will raise the Value event again.

GKH
  • 179
  • 1
  • 13
katfang
  • 2,010
  • 17
  • 13
30

For me in swift 5 use in another class:

import FirebaseFirestore
init?(document: QueryDocumentSnapshot) {
        let data = document.data()
       guard let stamp = data["timeStamp"] as? Timestamp else {
            return nil
        }
       let date = stamp.dateValue()
}
RAUL QUISPE
  • 800
  • 9
  • 13
  • 3
    This should be the accepted answer today. When setting data to store in Firebase, use Date. When retrieving it, use this answer to convert from Timestamp to Date. – Mike Taverne May 07 '20 at 07:06
12

This question is old, but I recently had the same problem so I'll provide an answer.

Here you can see how I am saving a timestamp to Firebase Database

 let feed = ["userID": uid,
             "pathToImage": url.absoluteString,
             "likes": 0,
             "author": Auth.auth().currentUser!.displayName!,
             "postDescription": self.postText.text ?? "No Description",
             "timestamp": [".sv": "timestamp"],
             "postID": key] as [String: Any]

 let postFeed = ["\(key)" : feed]

 ref.child("posts").updateChildValues(postFeed)

The particularly relevant line of code is "timestamp": [".sv": "timestamp"],

This saves the timestamp as a double in your database. This is the time in milliseconds so you need to divide by 1000 in order to get the time in seconds. You can see a sample timestamp in this image. Firebase Timestamp

To convert this double into a Date I wrote the following function:

func convertTimestamp(serverTimestamp: Double) -> String {
        let x = serverTimestamp / 1000
        let date = NSDate(timeIntervalSince1970: x)
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        formatter.timeStyle = .medium

        return formatter.string(from: date as Date)
    }

This gives a timestamp that looks like this: Timestamp

DoesData
  • 6,594
  • 3
  • 39
  • 62
3

You will get the right time if you use:

let timestamp = FIRServerValue.timestamp()

let converted = NSDate(timeIntervalSince1970: timestamp / 1000)


let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.dateFormat = "hh:mm a"
let time = dateFormatter.stringFromDate(converted)
TAREK
  • 41
  • 2
2
let serverTimeStamp = ServerValue.timestamp() as! [String:Any]

Store in Firebase something like [ktimeStamp:timestamp as AnyObject] than after you convert in seconds using Firebase Server Time:

let timestampDate = NSDate(timeIntervalSince1970: Double(timestamp as! NSNumber)/1000)
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Nik Jack
  • 87
  • 8
1

Firestore has an API for this --> -(NSDate *)dateValue

For example, if you have saved(set) a new document with a field "createdAtDate"

    NSDictionary *dataToBeSaved = @{
        //Tell the server to save FIRTimestamps when the document is created
        @"createdAtDate":[FIRFieldValue fieldValueForServerTimestamp],
        @"lastModifiedDate":[FIRFieldValue fieldValueForServerTimestamp],

       //Other fields
       @"userName":@"Joe Blow"
    }

    [myFirReference setData:[dataToBeSaved]
                    options:[FIRSetOptions merge]
                 completion:^(NSError* error) {
  }

You can get back this information either with a get query or via setting a listener. When you have the snapshot back, just access the dates you saved and convert to NSDate.

  NSDate *date1 = [snapshot.data[@"createdAtDate"] dateValue];
  NSDate *date2 = [snapshot.data[@"lastModifiedDate"] dateValue];

There will be a slight loss in precision, but as most people use dates for data synchronization or sorts, I can't think of a case where the loss of precision would be an issue.

Mike Critchley
  • 1,643
  • 15
  • 20
0

You can create a new transformer for ObjectMapper,

import Foundation
import ObjectMapper

class FirebaseDateTransform: TransformType {
    public typealias Object = Date
    public typealias JSON = Double

    open func transformFromJSON(_ value: Any?) -> Date? {
        if let millisecondsSince1970 = value as? Double {
            return Date(timeIntervalSince1970: millisecondsSince1970 / 1000.0)
        }

        return nil
    }

    open func transformToJSON(_ value: Date?) -> Double? {
        if let date = value {
            return Double(date.timeIntervalSince1970) * 1000.0
        }

        return nil
    }
}

Gist

phatmann
  • 18,161
  • 7
  • 61
  • 51
alicanbatur
  • 2,172
  • 1
  • 28
  • 36
0

Here is some code, based on alicanbatur's answer, that allows a date to be a Double or a server timestamp, and yet still work within an object mapping layer such as ObjectMapper.

enum FirebaseDate {
    case date(Date)
    case serverTimestamp

    var date: Date {
        switch self {
        case .date(let date):
            return date
        case .serverTimestamp:
            return Date()
        }
    }
}

class FirebaseDateTransform: TransformType {
    public typealias Object = FirebaseDate
    public typealias JSON = Any

    open func transformFromJSON(_ value: Any?) -> FirebaseDate? {
        switch value {
        case let millisecondsSince1970 as Double:
            let date = Date(millisecondsSince1970: millisecondsSince1970)
            return .date(date)
        case is [AnyHashable: Any]?:
            return .serverTimestamp
        default:
            return nil
        }
    }

    open func transformToJSON(_ value: FirebaseDate?) -> Any? {
        switch value {
        case .date(let date)?:
            return date.millisecondsSince1970
        case .serverTimestamp?:
            return ServerValue.timestamp()
        default:
            return nil
        }
    }
}
phatmann
  • 18,161
  • 7
  • 61
  • 51
0

You can get a date approximation from Firebase. For example if you're trying to change a firebase user's creation date (a Timestamp) to a Date:

user.creationDate.dateValue()
Noodybrank
  • 73
  • 1
  • 5
0

Swift 4 and updated Firebase library variation of Katfang's answer:

let currentTimeStamp: TimeInterval?

let ref = Database.database().reference().child("serverTimestamp")

ref.setValue(ServerValue.timestamp())

ref.observe(.value, with: { snap in
    if let t = snap.value as? TimeInterval {
    print(t/1000)
    currentTimeStamp = t/1000
    }
})
Aaron Halvorsen
  • 2,610
  • 1
  • 23
  • 31
0

If anyone is looking for the 2023 answer: Firebase has a Timestamp type, with a dateValue() method.

To convert from a Firestore to Swift Date object:

let time = document?.get("myTime") as? Timestamp
if let data = time?.dateValue() {
                print(data)
}