How to read a Firestore timestamp in Swift - swift

I'm trying to read a timestamp from my Firestore database and ultimately convert it to a date, but for some reason my code doesn't seem to be returning anything. Instead, it only seems to use the default value that I provide, which is 0, so it always reads as Jan 1, 1970.
The document I'm trying to read in Firestore includes a field called date, which has a type of timestamp. There are other fields in the document, but to keep things simple I've left those out from this question. For reference, the other fields from the document are successfully read.
I've tried the below code. Note that I have imported Firebase to the class:
surveyDataCollectionRef.whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "").getDocuments { (snapshot, error) in
if let err = error {
debugPrint("Error fetching docs: \(err)")
} else {
guard let snap = snapshot else { return }
for document in snap.documents {
let data = document.data()
let timestamp = data["date"] as? TimeInterval ?? 0
let date = Date(timeIntervalSince1970: timestamp)
dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
let strDate = dateFormatter.string(from: date)
let newSurvey = Survey(date: strDate)
self.surveys.append(newSurvey!)
self.currentSurveys = self.surveys
}
self.tableView.reloadData()
}
}
}

After even more trial and error I found that the below code seemed to be able to get me a date as a String value (and in the correct format that I was aiming for:
if let timestamp = data["date"] as? Timestamp {
let date = timestamp.dateValue()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .none
strDate = "\(dateFormatter.string(from: date))"
}

Related

Compare current system date with another date(which is coming as string format 2019-11-22) in swift

I'm getting date as a sting format from API (2019-11-22), I want to compare this date with current date.
I tried converting current date as string format this is success but this is not satisfying requiremet. I have to convert to String(2019-11-22) to Date and then I can compare two dates.
How can I convert string (2019-11-22) to Date to compare with system date pls help I'm lead knowledge in dates. Thanks in advance.
extension Date {
static func getCurrentDate() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.string(from: Date())
}
}
if Date() < apiEach["ExpiryDate"]! as! Date{
//apiEach["ExpiryDate"]! is 2019-11-22
pritn("You can proceed it's not outdated")
}
apiEach["ExpiryDate"] is a string (apiEach["ExpiryDate"] as! Date will crash) so you have two options:
Convert the current date to string
if Date.getCurrentDate() < apiEach["ExpiryDate"] as! String { ...
Convert the API string to Date and compare that
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd"
if let apiDate = dateFormatter.date(from: apiEach["ExpiryDate"] as! String),
Date() < apiDate { ...
func minimumDate(result:String) -> Bool {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let myDate = dateFormatter.date(from: "\(result)")
dateFormatter.dateFormat = "yyyy-MM-dd"
let now = Date()
let startDateComparisionResult:ComparisonResult = now.compare(myDate!)
if startDateComparisionResult == ComparisonResult.orderedAscending {
print("Current date is smaller than end date.")
let somedateString = dateFormatter.string(from: myDate!)
print(somedateString)
return true
}
else if startDateComparisionResult == ComparisonResult.orderedDescending {
// Current date is greater than end date.
print("Current date is greater than end date.")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date1String = dateFormatter.string(from: myDate!)
let date2String = dateFormatter.string(from: now)
if date1String == date2String {
print("Equal date")
return true
}
return false
}
else if startDateComparisionResult == ComparisonResult.orderedSame {
// Current date and end date are same
print("Current date and end date are same")
return true
}
return true
}
Since the date format "yyyy-MM-dd" can be properly sorted/compared you can either convert current date to a string and compare it with your API value
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let now = dateFormatter.string(from: Date())
switch now.compare(input) { //input is from API
case .orderedSame:
print("Same")
case .orderedAscending:
print("Before")
case .orderedDescending:
print("After")
}
If you want to compare dates it is important that Date() will also include the current time while a date converted using a date formatter with a date only format will have its time zeroed out (midnight) so the comparison might not be correct if the date part is the same. To handle this it is better to use DateComponents
let calendar = Calendar.current
guard let date = dateFormatter.date(from: input) else { return //or error }
let components = calendar.dateComponents([.year, .month, .day], from: now, to: date)
if let year = components.year, let month = components.month, let day = components.day {
switch (year + month + day) {
case 0: // all values are zero
print("Same")
case ..<0 : // values will be zero or negative so sum is negative
print("After")
default: // values will be zero or positive so sum is positive
print("Before")
}
}

How to store date in userdefaults?

I'd like to record the history of her entry into the textfield. I intend to register this with UserDefaults. But when I try to save it with UserDefaults, "cannot assign value of type 'nsdate'?'to type 'String' " Error. I don't think it's accepting textfield data because it's string. And how can I keep history in memory?
formatteddate and formatteddate2 give this error. The date output is as follows : 20/04/2019 20:23
let token = try? keychain.getString("chipnumbernew")
chip1InfoString = token
var time = NSDate()
var formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy HH:mm"
var formatteddate = formatter.string(from: time as Date)
var formatteddate2 = formatter.string(from: time as Date)
timertextNew.text = formatteddate
timertext2New.text = formatteddate2
let RetrivedDate = UserDefaults.standard.object(forKey: "timertext") as? NSDate
formatteddate = RetrivedDate
let RetrivedDate2 = UserDefaults.standard.object(forKey: "timertext2") as? NSDate
formatteddate2 = RetrivedDate2
If you only want to display the date value you can convert and store it as string otherwise you convert/format it after you have read it, either way you should make sure you use the same type when saving and reading
//save as Date
UserDefaults.standard.set(Date(), forKey: key)
//read
let date = UserDefaults.standard.object(forKey: key) as! Date
let df = DateFormatter()
df.dateFormat = "dd/MM/yyyy HH:mm"
print(df.string(from: date))
// save as String
let df = DateFormatter()
df.dateFormat = "dd/MM/yyyy HH:mm"
let str = df.string(from: Date())
UserDefaults.standard.setValue(str, forKey: key)
// read
if let strOut = UserDefaults.standard.string(forKey: key) {
print(strOut)
}
The following saves a Date object as a Double (a.k.a. TimeInterval). This avoids any date formatting. Formatting can lose precision, and is unnecessary since the string is not intended for users to read.
// save
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: key)
// read
let date = Date(timeIntervalSince1970: UserDefaults.standard.double(forKey: key))
you can always create an extension for saving and retrieving the date in userdefault. here the example code:
import Foundation
extension UserDefaults {
func set(date: Date?, forKey key: String){
self.set(date, forKey: key)
}
func date(forKey key: String) -> Date? {
return self.value(forKey: key) as? Date
}
}
let userDefault = UserDefaults.standard
userDefault.set(date: Date(), forKey: "timertext")
print(userDefault.date(forKey: "timertext") ?? "")
Your problem is that you are retrieving NSDate from the user default storage and then trying to assign them to a String (formatteddate).
Try this;
formatteddate = formatter.string(from: RetrivedDate as Date)

Converting timestamp from Firebase and adding to array

I'm trying to read a timeStamp from Firebase and append it to an array.
I have made some progress:
var orderDateHistoryArray = [String:Int]()
func getOrderDates() {
let uid = Auth.auth().currentUser!.uid
let orderDateHistoryRef = Database.database().reference().child("users/\(uid)/Orders/")
orderDateHistoryRef.observeSingleEvent(of: .value, with: { (snapshot) in
// Get dates
let value = snapshot.value as? NSDictionary
if let orderDate = value?["Date"] as? [String:Int] {
self.orderDateHistoryArray += Array(orderDate.values)//This does not conform
print(orderDate)
}
self.tableView.reloadData()
// ...
}) { (error) in
print(error.localizedDescription)
}
}
The print(orderDate)statement prints:
["-LQYspEghK3KE27MlFNE": 1541421618601,
"-LQsYbhf-vl-NnRLTHhK": 1541768379422,
"-LQYDWAKlzTrlTtO1Qiz": 1541410526186,
"-LQsILjpNqKwLl9XBcQm": 1541764115618]
This is childByAutoID : timeInMilliseconds
So, I want to read out the timeInMilliseconds, convert it to a readable timestampand append it to the orderDateHistoryArray
Those are timestamps. Parse it as a Date object then use .toLocaleDateString to get date.
alert( new Date(1541421618601).toLocaleDateString("en-US") );
In order to transform your timestamp you must, first remove milliseconds on each the values returned by the dictionary.
self.orderDateHistoryArray += Array(orderDate.values).map { Date(timeIntervalSince1970: TimeInterval($0/1000)) }
In order to get it in a "human way", you need to have a DateFormatter. It's on this object where you define how it's presented.
extension Date {
func format(_ dateFormat: String = "dd/MMMM")
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "MMMM dd"
return formatter.string(from: self)
}
}
and on a Date element you can just call it by date.format() or by passing a string date.format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX")

Swift - How to store multiple date in default and retrieve it?

This is my code. I want to save todayString and nextString in user default so that I can display the stored date (update every Tuesday) in label when it's not Tuesday.
let today = NSDate()
let nextTue = Calendar.current.date(byAdding: .day, value: 6, to: today as Date)
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let todayString = formatter.string(from: today as Date)
let nextString = formatter.string(from: nextTue!)
formatter.dateFormat = "dd-MMM-yyyy"
let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)
let components = calendar!.components([.weekday], from: today as Date)
if components.weekday == 3 {
print("Hello Tuesday")
thisWeekDate.text! = "\(todayString) - \(nextString)"
} else {
print("It's not Tuesday")
}
NSUserDefaults saves data between runs of apps using the device storage.
The easiest way in your case would be to save two different date objects, or you can create a dictionary containing both date objects and save it instead.
Store
UserDefaults.standard.set(todayString, forKey: "todayStringKey")
Retrieve
let RetrivedDate = UserDefaults.standard.object(forKey: "todayStringKey") as? NSDate
Remove - in case you want to delete it completely from storage
UserDefaults.standard.removeObject(forKey: "todayStringKey")

Swift displaying the time or date based on timestamp

I have an API that returns data including a timestamp for that record.
In swift I have the timestamp element loaded and converted into a double and can then convert that into time. I want to be able to return the time if the date of the record is today and return the date if the record is not today.
See below:
let unixTimeString:Double = Double(rowData["Timestamp"] as! String)!
let date = NSDate(timeIntervalSince1970: unixTimeString) // This is on EST time and has not yet been localised.
var dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .ShortStyle
dateFormatter.doesRelativeDateFormatting = true
// If the date is today then just display the time, if the date is not today display the date and change the text color to grey.
var stringTimestampResponse = dateFormatter.stringFromDate(date)
cell.timestampLabel.text = String(stringTimestampResponse)
Do I use NSCalendar to see if 'date' is today and then do something?
How do you then localise the time so that its correct for the user rather than server time?
There is a handy function on NSCalendar that tells you whether an NSDate is in today or not (requires at least iOS 8) isDateInToday()
To see it working, put this into a playground:
// Create a couple of unix dates.
let timeIntervalToday: NSTimeInterval = NSDate().timeIntervalSince1970
let timeIntervalLastYear: NSTimeInterval = 1438435830
// This is just to show what the dates are.
let now = NSDate(timeIntervalSince1970: timeIntervalToday)
let then = NSDate(timeIntervalSince1970: timeIntervalLastYear)
// This is the function to show a formatted date from the timestamp
func displayTimestamp(ts: Double) -> String {
let date = NSDate(timeIntervalSince1970: ts)
let formatter = NSDateFormatter()
formatter.timeZone = NSTimeZone.systemTimeZone()
if NSCalendar.currentCalendar().isDateInToday(date) {
formatter.dateStyle = .NoStyle
formatter.timeStyle = .ShortStyle
} else {
formatter.dateStyle = .ShortStyle
formatter.timeStyle = .NoStyle
}
return formatter.stringFromDate(date)
}
// This should just show the time.
displayTimestamp(timeIntervalToday)
// This should just show the date.
displayTimestamp(timeIntervalLastYear)
Or, if you just want to see what it looks like without running it yourself:
Abizern's answer in Swift 3:
import UIKit
let timeIntervalToday: TimeInterval = Date().timeIntervalSince1970
let timeIntervalLastYear: TimeInterval = 1438435830
// This is just to show what the dates are.
let now = Date(timeIntervalSince1970: timeIntervalToday)
let then = Date(timeIntervalSince1970: timeIntervalLastYear)
// This is the function to show a formatted date from the timestamp
func displayTimestamp(ts: Double) -> String {
let date = Date(timeIntervalSince1970: ts)
let formatter = DateFormatter()
//formatter.timeZone = NSTimeZone.system
if Calendar.current.isDateInToday(date) {
formatter.dateStyle = .none
formatter.timeStyle = .short
} else {
formatter.dateStyle = .short
formatter.timeStyle = .none
}
return formatter.string(from: date)
}
// This should just show the time.
displayTimestamp(ts: timeIntervalToday)
// This should just show the date.
displayTimestamp(ts: timeIntervalLastYear)