current time

I used the Date class such as "currentTime = Date()", but I get a date that is off by 5 hours exactly. How do I get the current time? Is there a time zone setting I have to set?

Accepted Reply

I think you are trying to do too much work.


Imagine that you have an old fashioned watch on your wrist, set to the correct time in the CMT time zone.

You are attacked by a ninja, who knocks you unconcious and puts you on a plane to Siberia.

You wake up, with no idea what the time difference is between CMT and whatever timezone this part of Siberia is in.

The ninja tells you that you will freeze to death in one hour.


If you look at the current time in CMT on your watch, can you figure out later exactly how much time you have left until that one hour is up? Or do you need to know the correct timezone you are in?



Instances of Date or NSTimer don't really care about the timezone, they only care about how much time passes.


So the current Date() doesn't need to know or care what a timezone is, it only cares how many seconds have passed since a specific point in time that it uses for reference. NSTimer doesn't need to know or care what a timezone is, it just needs to trigger the code when the right number of seconds have passed since that same specific point in time that it uses for reference.


The description property of a Date uses GMT when it converts itself to a string, which doesn't work so well for people who want to compare that date to their own timezone. That's why a DateFormatter can give you a localized string with the "correct" time, to display for users. But the Date itself isn't in GMT, it is just a reference to a number of seconds, which pass in the same amount of time no matter what the timezone is.

Replies

Here's the way that the Swift Foundation library formats the date to display in playgrounds:

extension Date
{
    var summary: String
    {
        let df = DateFormatter()
        df.dateStyle = .medium
        df.timeStyle = .short
        return df.string(from: self)
    }
}


let time = Date().summary

The date formatter automatically gets the user's locale setting to adjust the displayed time.

You have effectively a time zone question (you're in India time zone probably).


You have to use dayFormatter.dateFormat ; here is an example


func formattedDay(_ aDate: Date, stripped: Bool) -> String {
    let dayFormatter = DateFormatter()
    // French Locale (fr_FR) Select your locale for where you are : for India it is hi-IN or hi_IN
    dayFormatter.locale = Locale(identifier: "fr_FR") // Would be "ja_JP" for Japan

    if stripped {
        dayFormatter.dateFormat = "dd/MM/yyyy"
    } else {
        dayFormatter.dateFormat = "dd MMM yyyy"
    }
    let dateString = dayFormatter.string(from: aDate)
    return dateString
}


func formattedTime(_ aDate: Date, stripped: Bool) -> String { /

    let dayFormatter = DateFormatter()
    dayFormatter.timeStyle = .none;
    dayFormatter.locale = Locale(identifier: "fr_FR") // Would be "ja_JP" for Japan 
    if stripped {
        dayFormatter.dateFormat = "Hmm"
    } else {
        dayFormatter.dateFormat = "HH:mm"
    }
    let timeString = dayFormatter.string(from: aDate)
    return timeString
}

You have to use dayFormatter.dateFormat

Setting fixed date format strings is almost never the right answer; worse yet, code like this often works on your machine and then fails in mysterious circumstances for users in the field. For example:

  • Your formattedDay(_:stripped:) method will fail if the user is using the Japanese calendar, where the year doesn’t make much sense without the era

  • Your formattedTime(_:stripped:) will do odd things if the user has overridden the 12/24 hour setting for their locale

It’s best to stay out of this minefield completely by restricting your use of DateFormatter to one of these three techniques:

  • Standard formats, obtained by setting the dateStyle and timeStyle properties

  • Formats containing specific fields, by passing those fields to DateFormatter.dateFormat(fromTemplate:options:locale:) and then applying the resulting string to the dateFormat property of your date formatter

  • Fixed formats, in which case you must set the locale to en_US_POSIX (see QA1480 NSDateFormatter and Internet Dates for details)

There are convenience methods for the first two, namely:

  • DateFormatter.localizedString(from:dateStyle:timeStyle:)

  • setLocalizedDateFormatFromTemplate(_:)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"

Yes. I had figured out how to use the DateFormatter, but I need a variable of type Date to plug into a method. The DateFormatter yields the correct time as a String, but when I convert the string back to a Date type it reverts back to the time in Greenwich Mean Time.

I'm actually in Texas. I was able to use the DateFormatter to give me the correct time. I had set the timeZone property to "CDT" and it worked. However, when I convert the string result back to a type Date, the result reverted back to Greenwich Mean Time.

Is there a way to get the date and time without the DateFormatter, in the same way that I get the Greenwich Mean Time with the statement "let date = Date()"? I don't need a string value. I need a value of type Date to use with another method.

The missing piece of information is this: "Date" objects aren't actually a date or a time, they're an elapsed number of seconds since 00:00:00 UTC[GMT] on January 1, 2001.


IOW, what date or time "Date ()" refers to depends on where you are in the world and what calendar system you're using. To get year, month, day, hour, minute second, you need to start from a Calendar object set to the correct locale and time zone, and then there are methods for getting the component values you need.


The DateFormatter does the same thing, but then goes on to format the components according to your current language and locale.

I've tried using the DateFormatter and the Calendar classes. I am able to get the correct date according to the time zone my device is set to as a string value or as date components, but never as a value of type Date. I need a value of type Date in order to set the fireAt argument of the Timer initializer, which takes a value of type Date.

Thanks for the explanation, I'll revisit my DataFormatting.


Would this be correct in all cases ?


func formattedDay(_ aDate: Date, stripped: Bool) -> String { /
    let current = NSLocale.current.identifier
    let dayFormatter = DateFormatter()
    dayFormatter.locale = Locale(identifier: current)
    let dateStyle : DateFormatter.Style = stripped ? .short : .medium
    let dateString = DateFormatter.localizedString(from: aDate, dateStyle:dateStyle, timeStyle: .none)
    return dateString
}

If you want to set the timer to fire at "now plus 1 second", or "now plus 24 hours", you can just add the relevant time interval (in seconds) to Date ().


If you want to set the time to fire at a specific date and/or time ("at 3 pm today", or "on June 30th"), you need to do the reverse of what I described. Starting from your desired date/time, use CalendarComponents/Calendar with the right locale and time zone to create a Date object that represent the date/time.


This is complicated, because timekeeping is complicated and irregular. (Think of leap years, leap seconds, daylight savings time adjustments, calendar reforms, etc, etc.) The only easy thing is elapsed time in seconds, which is why the Date type uses that.

An instance of Date is a particular universal time, regardless of the timezone that is being used.


For example, 11:25 PDT today is identical to 14:25 EDT today, and the Date instance would have the same number of elapsed seconds.


If you need a Date for a short term timer, the easiest way to get one is to use

Date(timeIntervalSinceNow: delayInSeconds)

If you need a long term timer, you would probably want to use Calendar as Quincey described above.

Let me see. I'll try it. Thanks for helping me out.

I tried out your code with modifications. It turns out that assigning the current locale from NSLocale to an object of type DateFormatter doesn't change the results of the localizedString of the DateFormatter object. Here is my modified code based on your code:


class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        /
     
        let date = Date()
     
        print(localizedDay(date, stripped: false))
        print(nonLocalizedDay(date, stripped: false))
     
    }

    func localizedDay(_ aDate: Date, stripped: Bool) -> String {
        let current = NSLocale.current.identifier
        let dayFormatter = DateFormatter()
        dayFormatter.locale = Locale(identifier: current)
        let dateStyle : DateFormatter.Style = stripped ? .short : .full
        let dateString = DateFormatter.localizedString(from: aDate, dateStyle:dateStyle, timeStyle: .full)
        return dateString
    }

    func nonLocalizedDay(_ aDate: Date, stripped: Bool) -> String {
        let dayFormatter = DateFormatter()
        let dateStyle : DateFormatter.Style = stripped ? .short : .full
        let dateString = DateFormatter.localizedString(from: aDate, dateStyle:dateStyle, timeStyle: .full)
        return dateString
    }

}


The debug console shows the same result whether you re-assinged the locale or not. Here it is:


Wednesday, June 21, 2017 at 1:52:12 PM Central Daylight Time

Wednesday, June 21, 2017 at 1:52:12 PM Central Daylight Time

Whether I add the relevant interval to set the timer or I set the timer to fire at a specific date, the timer requires a value of type Date. That still means I have to use a variable of type Date, and that variable still goes by the absolute date, which is in Greenwich Mean Time and not accroding to the time zone my device is set to.


It looks like I need to use the timer without setting a start date.

I just tried your suggestion. The value I get from Date(timeIntervalSinceNow: delayInSeconds) is still in absolute time or Greenwich Mean Time. Your suggestion won't work unless I use code to get the time zone the device is set to and calculate the difference in time interval between Greenwich Mean Time and the time zone the device is set to, then plug that time interval as the argument for Date(timeIntervalSinceNow:). That would require a lot of code, because I would have to have a table for the difference between each time zone and Greenwich Mean Time. I wonder if there's a method in the Calendar class that allows me to get the time interval between each time zone and Greenwich Mean Time?