NSDate() or Date() shows the wrong time
Asked Answered
C

4

10

When I try to log the current date:

print(NSDate())

or

print(Date()) 

(in Swift 3)

Or any date object, it shows the wrong time. For example, it's about 16:12 now, but the above displayed

2016-10-08 20:11:40 +0000

Is my date in the wrong time zone? How do I fix my date to have the correct time zone?

Why is that, and how to I fix it? How do I easily display an arbitrary date in my local time zone, either in print statements or in the debugger?

(Note that this question is a "ringer" so that I can provide a simple Swift 3/Swift 2 Date/NSDate extension that lets you easily display any date object in your local time zone.

Corporation answered 8/10, 2016 at 20:26 Comment(0)
C
24

NSDate (or Date in Swift ≥ V3) does not have a time zone. It records an instant in time all over the world.

Internally, date objects record the number of seconds since the "epoch date", or Midnight on January 1, 2001 in Greenwich Mean Time, a.k.a UTC.

We normally think of dates in our local time zone.

If you log a date using

print(NSDate()) 

The system displays the current date, but it expresses it in UTC/Greenwich Mean Time. So the only place the time will look correct is in that time zone.

You get the same issue in the debugger if you issue the debugger command

e NSDate()

This is a pain. I personally wish iOS/Mac OS would display dates using the user's current time zone, but they don't.

EDIT #2:

An improvement on my previous use of localized string that makes it a little easier to use is to create an extension to the Date class:

extension Date {
    func localString(dateStyle: DateFormatter.Style = .medium, timeStyle: DateFormatter.Style = .medium) -> String {
        return DateFormatter.localizedString(from: self, dateStyle: dateStyle, timeStyle: timeStyle)
    }
}

That way you can just use an expression like Date().localString(), or if you want to only print the time, you can use Date().localString(dateStyle:.none)

EDIT:

I just discovered that NSDateFormatter (DateFormatter in Swift 3) has a class method localizedString. That does what my extension below does, but more simply and cleanly. Here is the declaration:

class func localizedString(from date: Date, dateStyle dstyle: DateFormatter.Style, timeStyle tstyle: DateFormatter.Style) -> String

So you'd simply use

let now = Date()
print (DateFormatter.localizedString(
  from: now, 
  dateStyle: .short, 
  timeStyle: .short))

You can pretty much ignore everything below.


I have created a category of the NSDate class (Date in swift 3) that has a method localDateString that displays a date in the user's local time zone.

Here is the category in Swift 3 form: (filename Date_displayString.swift)

extension Date {
  @nonobjc static var localFormatter: DateFormatter = {
    let dateStringFormatter = DateFormatter()
    dateStringFormatter.dateStyle = .medium
    dateStringFormatter.timeStyle = .medium
    return dateStringFormatter
  }()
  
  func localDateString() -> String
  {
    return Date.localFormatter.string(from: self)
  }
}

And in Swift 2 form:

extension NSDate {
   @nonobjc static var localFormatter: NSDateFormatter = {
    let dateStringFormatter = NSDateFormatter()
    dateStringFormatter.dateStyle = .MediumStyle
    dateStringFormatter.timeStyle = .MediumStyle
    return dateStringFormatter
  }()
  
public func localDateString() -> String
  {
    return NSDate.localFormatter.stringFromDate(self)
  }
}

(If you prefer a different date format it's pretty easy to modify the format used by the date formatters. It's also straightforward to display the date and time in any timezone you need.)

I would suggest putting the appropriate Swift 2/Swift 3 version of this file in all of your projects.

You can then use

Swift 2:

print(NSDate().localDateString())

Swift 3:

print(Date().localDateString())
Corporation answered 8/10, 2016 at 20:26 Comment(2)
If you're trying to represent a canonical answer that employs a static variable, then I think you better respond to NSLocale.currentLocaleDidChangeNotification (a.k.a. NSCurrentLocaleDidChangeNotification) and invalidate/reset cached formatters, too. If you don't want to go down that rabbit hole, then maybe the extension with a static doesn't belong as part of this "canonical" answer, and just leave it at "use DateFormatter like so ...".Yoakum
Rob, point taken. I guess "canonical answer" was a bit much, given that the user could change locales as you point out. More like portable, easy solution to a common development/debugging problem. Since this is primarily for development support, switching locales doesn't really seem like an issue.Corporation
B
3

A simple way to correct the Date for your timezone would be to use TimeZone.current.secondsFromGMT()

Something like this for a local timestamp value for example:

let currentLocalTimestamp = (Int(Date().timeIntervalSince1970) + TimeZone.current.secondsFromGMT())

Becky answered 17/7, 2019 at 1:37 Comment(3)
it will not give us accuracy upto mili or neno secondsWorks
This is exactly what you should NEVER do.Caiman
Ugh. No. This is not a good way to deal with time zones.Corporation
R
1

Just for logging or printing the date in the current time zone use description(with:)

print(Date().description(with: .current)

This API is available back to iOS 8+ and macOS 10.10+

Rein answered 4/10, 2023 at 19:42 Comment(0)
B
0

A much simpler answer for 2023

// for the standard Apple look
let nowish = DateFormatter.localizedString(
                    from: Date(), dateStyle: .short, timeStyle: .short)
print(nowish)

result 10/28/23

// for a custom format. you need all four lines of code, in this order:
let ft = DateFormatter()
ft.locale = Locale.current
ft.setLocalizedDateFormatFromTemplate("MM/dd/yyyy")
let nowish = ft.string(from: Date())
print(nowish)

result 10/28/2023

Take care that one formula has "localizedString" and the other formula has "string".

You often want the "ten days from now, last year, etc" approach. Simply use Calendar.current in that case.

 let nextYr = Calendar.current.date(byAdding: .year, value: 2, to: Date())!

Then simply use nextYr instead of Date() in the above formulas.

Boron answered 4/10, 2023 at 19:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.