Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 4 : Set Different Date and Time

I know how to get local date and time, but what I want to do is getting the date and time from different places. For example, I want to find out what the time and date is in New York. How can i solve this simple problem?

Here is my code for local date and time :

let date = NSDate()
let calendar = Calendar.current

let components = calendar.dateComponents([.hour, .minute, .month, .year, .day, .second, .weekOfMonth], from: date as Date)

let currentDate = calendar.date(from: components) 

I searched about it here, but i didn't find what i want and I'm still looking for the date libaries. If you know any source or sample to redirect me, I really appreciate that.

like image 616
jorjj Avatar asked Nov 13 '17 18:11

jorjj


3 Answers

There are several different concepts involved here, and we need to understand (almost) all of them to get this right...

1) a Date (NSDate as was, in Swift) is an absolute point in time - it's slightly mis-named, because it has nothing to do with an actual date like 13th November 2017, because to get to that we need to define ...

2) a Calendar, because 13th November 2017 in the western Gregorian calendar could also be 23rd Safar 1439 in the Islamic calendar, or the 24th of Heshvan 5778 in the Hebrew calendar, or some other things in the many other calendars that iOS & MacOS support;

3) in turn Calendar changes not only what values are returned in the DateComponents that we have to use to unpack a Date + Calendar into days, months, years & eras (e.g. BC/AD), or even week number, etc..., but also some calendars might not have the same components as others;

4) time-of-day (as you know) depends on TimeZone, so the same absolute time can be one of many different times "o'clock" depending on where you are. It may also (as you can see in the example below) change the date as well as the "o'clock". This of course could be automatic (where you are) or set by the programmer;

5) further, we have DateFormatter (which is a convenience that wraps up DateComponents), because 13th November 2017 could be represented as 13/11/17 or 11/13/17 depending on whether you are British or American. We may also wish to choose whether we use text or numeric months, and, if displaying times, whether we want 12 hour or 24 hour format - all of these are covered by DateFormatter, but text representation may be "13e Novembre 2017" if you are French, which introduces the notion of

6) Locale, which can be set, like TimeZone, as being default (as chosen when you set up the device) or specified by the programmer.

The code you posted won't work, because all it does is takes a Date, transforms it through a Calendar to DateComponents (all good so far), but then recreates a Date from the components - all you will get is the original Date - the same absolute point in time.

What I believe from the question and your answers to questions in the comments is that you want a function that takes an absolute time (eg "now") aka a Date and displays it in a specific TimeZone. This works:

func timeComponents(date: Date, timeZone: TimeZone) -> DateComponents {
    var calendar = Calendar.current
    calendar.timeZone = timeZone
    return calendar.dateComponents([.hour, .minute, .month, .year, .day, .second, .weekOfMonth], from: date)
}

let absTime: Date = Date() // Now
let edinburgh = TimeZone(abbreviation: "GMT")!
let newYork = TimeZone(abbreviation: "EST")!
let ec = timeComponents(date: absTime, timeZone: edinburgh)
let nycc = timeComponents(date: absTime, timeZone: newYork)
print(ec)// year: 2017 month: 11 day: 14 hour: 0 minute: 44 second: 10 weekOfMonth: 3 isLeapMonth: false 
print(nycc) // year: 2017 month: 11 day: 13 hour: 19 minute: 44 second: 10 weekOfMonth: 3 isLeapMonth: false 

... which I think answers the minimum of your question, but to finesse it, we need to move from DateComponents to DateFormatter

func timeString(date: Date, timeZone: TimeZone, timeStyle: DateFormatter.Style) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = timeZone
    dateFormatter.dateStyle = .none
    dateFormatter.timeStyle = timeStyle
    return dateFormatter.string(from: date)
}

let es = timeString(date: absTime, timeZone: edinburgh, timeStyle: .full)
let nycs = timeString(date: absTime, timeZone: newYork, timeStyle: .full)
print(es) // 12:44:10 AM Greenwich Mean Time
print(nycs) // 7:44:10 PM Eastern Standard Time

You can go on, and start to use Locale, if you want to internationalise your app, but I'l leave that as an exercise!

p.s. These are not all of the concepts - see here

p.p.s. See also this answer and this answer (neither duplicates)

like image 110
Grimxn Avatar answered Oct 08 '22 14:10

Grimxn


If you just want to format the date to a string, consider using a DateFormatter instead:

let date = Date()
let formatter = DateFormatter()
formatter.timeZone = TimeZone(identifier: "America/New_York")
formatter.dateStyle = .long
formatter.timeStyle = .long
formatter.string(from: date)

If you want to get the date components and process them, use the dateComponents(in:from:) method.

let components = Calendar.current.dateComponents(in: TimeZone(identifier: "America/New_York")!, from: date)
like image 6
Sweeper Avatar answered Oct 08 '22 16:10

Sweeper


If you don't know the time zone of the place you are searching for, you can use the CoreLocation's CLGeocoder and search on an address string. Then you can get the timezone for that place and translate that into the time you're looking for:

let geocoder = CLGeocoder()
geocoder.geocodeAddressString("New York, New York") { (placemarks, error) in
    guard error == nil else {
        print("Error")
        print(error!.localizedDescription)
        return
    }
    guard let placemarks = placemarks,
        let place = placemarks.first else {
        print("No results")
        return
    }

    if let timeZone = place.timeZone {
        print("TimeZone: \(timeZone.identifier)")
        // TimeZone: America/New_York

        //Ignore the time zone offset from this one, it will be the difference between the current time and the new york time
        let dateInNewYork = Date().addingTimeInterval(TimeInterval.init(timeZone.secondsFromGMT()))
        print(dateInNewYork)
        // 2017-11-13 15:03:05 +0000

        //Or
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(identifier: timeZone.identifier)
        formatter.dateStyle = .long
        formatter.timeStyle = .long
        let formattedDateInNewYork = formatter.string(from: Date())
        print(formattedDateInNewYork)
        // November 13, 2017 at 3:03:05 PM EST

        //Or
        let components = Calendar.current.dateComponents(in: TimeZone(identifier: timeZone.identifier)!, from: Date())
        print(components.date!)
        // 2017-11-13 20:03:05 +0000
    }

}
like image 2
Zig Avatar answered Oct 08 '22 16:10

Zig