iOS Swift - Get the Current Local Time and Date Timestamp

I'm trying to make an attendance app and I am really confused about date and time in iOS and Firebase.

I use date as Key, this is the structure of my Firebase database.

--Employees
  --Unique_ID
     --Details
          Name: John
     --Attendance
          --dateToday
              Timein: 8:00 AM
              Timeout: 5:00 PM
              BreakStart: 12:00 PM
              BreakFinish: 1:00 PM

This is my code to get the date timestamp I used as Key

 override func viewDidLoad() {
     super.viewDidLoad()

     let now = NSDate()
     let nowTimeStamp = self.getCurrentTimeStampWOMiliseconds(dateToConvert: now)

     // I save this dateToday as Key in Firebase
     dateToday = nowTimeStamp
}


func getCurrentTimeStampWOMiliseconds(dateToConvert: NSDate) -> String {
    let objDateformat: DateFormatter = DateFormatter()
    objDateformat.dateFormat = "yyyy-MM-dd"
    let strTime: String = objDateformat.string(from: dateToConvert as Date)
    let objUTCDate: NSDate = objDateformat.date(from: strTime)! as NSDate
    let milliseconds: Int64 = Int64(objUTCDate.timeIntervalSince1970)
    let strTimeStamp: String = "\(milliseconds)"
    return strTimeStamp
}

But when I convert it back to date I get 2017-09-22 16:00:00 +0000, which is wrong because it is 23rd of September in my location.

What is the right code to use so that I can get the correct date timestamp and time timestamp?

Answers


For saving Current time to firebase database I use Unic Epoch Conversation:

let timestamp = NSDate().timeIntervalSince1970

and For Decoding Unix Epoch time to Date().

let myTimeInterval = TimeInterval(timestamp)
let time = NSDate(timeIntervalSince1970: TimeInterval(myTimeInterval))

If you just want the unix timestamp, create an extension:

extension Date {
    func currentTimeMillis() -> Int64 {
        return Int64(self.timeIntervalSince1970 * 1000)
    }
}

Then you can use it just like in other programming languages:

let timestamp = Date().currentTimeMillis()

First I would recommend you to store your timestamp as a NSNumber in your Firebase Database, instead of storing it as a String.

Another thing worth mentioning here, is that if you want to manipulate dates with Swift, you'd better use Date instead of NSDate, except if you're interacting with some Obj-C code in your app.

You can of course use both, but the Documentation states:

Date bridges to the NSDate class. You can use these interchangeably in code that interacts with Objective-C APIs.

Now to answer your question, I think the problem here is because of the timezone.

For example if you print(Date()), as for now, you would get:

2017-09-23 06:59:34 +0000

This is the Greenwich Mean Time (GMT).

So depending on where you are located (or where your users are located) you need to adjust the timezone before (or after, when you try to access the data for example) storing your Date:

    let now = Date()

    let formatter = DateFormatter()

    formatter.timeZone = TimeZone.current

    formatter.dateFormat = "yyyy-MM-dd HH:mm"

    let dateString = formatter.string(from: now)

Then you have your properly formatted String, reflecting the current time at your location, and you're free to do whatever you want with it :) (convert it to a Date / NSNumber, or store it directly as a String in the database..)


The simple way to create Current TimeStamp. like below,

func generateCurrentTimeStamp () -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
    return (formatter.string(from: Date()) as NSString) as String
}

in Swift 5

extension Date {
    static var currentTimeStamp: Int64{
        return Int64(Date().timeIntervalSince1970 * 1000)
    }
}

call like this:

let timeStamp = Date.currentTimeStamp
print(timeStamp)

Thanks @lenooh


If you code for iOS 13.0 or later and want a timestamp, then you can use:

let currentDate = NSDate.now

When you convert a UTC timestamp (i.e. 2017-11-06 20:15:33 -08:00) into a Date() object in Swift, Swift zeroes out the time zone to GMT (which is plus or minus 0). For calculating a time interval, this doesn't pose a problem because however many hours (and minutes, as there a number of time zones that are not divided on the hour, but at :45 or :30) are added or subtracted from zeroing out the time zone are added or subtracted to the date's hour (and sometimes minute) value, so the absolute point in time never changes. However, when converting that date object into a string, using DateFormatter() or ISO8601DateFormatter(), that does pose a problem because the original time zone was zeroed out.

Now, I favor using RFC3339 (i.e. 2017-11-06T20:15:33-08:00), the internet-friendly format for the ISO8601 standardized time format. Chances are that 3rd-party API you'll one day hook up to will use it so I think we should all be using it now. The format in Swift is yyyy-MM-dd'T'HH:mm:ssXXXXX. Swift also has a ISO8601DateFormatter() that can convert string literals into date objects very simply:

func getDateFromUTC(RFC3339: String) -> Date? {
    let formatter = ISO8601DateFormatter()
    return formatter.date(from: RFC3339)
}

And using the RFC3339 format also makes extracting the time zone very simple:

func getTimeZoneFromUTC(RFC3339: String) -> TimeZone? {
    switch RFC3339.suffix(6) {
    case "+05:45":
        return TimeZone(identifier: "Asia/Kathmandu")
    default:
        return nil
    }
}

Of course, you would have to fill in the other 37 or so time zones using whatever methods suit you best. And now if you want to format that string literal into something more user friendly, you can combine the two methods above into something like this:

func getFormattedDateFromUTC(RFC3339: String) -> String? {
    guard let date = getDateFromUTC(RFC3339: RFC3339),
        let timeZone = getTimeZoneFromUTC(RFC3339: RFC3339) else {
            return nil
    }
    let formatter = DateFormatter()
    formatter.dateFormat = "h:mma EEE, MMM d yyyy"
    formatter.amSymbol = "AM"
    formatter.pmSymbol = "PM"
    formatter.timeZone = timeZone // preserve local time zone
    return formatter.string(from: date)
}

And so this string "2018-11-06T17:00:00+05:45", which expresses 5:00PM somewhere in Kathmandu, will print 5:00PM Tue, Nov 6 2018, displaying the local time, regardless of where the machine is. And, of course, if you don't want to format it using its contextual local time, simply don't set the timeZone property.

As a side note in response to some of the suggestions in this thread, I would recommend storing dates as strings in Firebase. Storing dates as strings, particularly using a format like RFC3339, makes this data platform agnostic. You can switch to any database on any platform in any framework and you wouldn't need to change any logic because strings are always just strings. And RFC3339 is the one profile that basically any framework worth its salt recognizes.


Need Your Help

Unexpectedly poor and weirdly bimodal performance for store loop on Intel Skylake

performance assembly optimization x86 x86-64

I'm seeing unexpectedly poor performance for a simple store loop which has two stores: one with a forward stride of 16 byte and one that's always to the same location1, like this:

Unable to remove an in-app subscription from my google play developer console

android in-app-purchase google-play

I have published a new version of my app, and although it has no In-app functionality or billing permission, the Google play page of my app shows that the app has a 0.50cent in-app item for purchase.