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.
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())
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())
Related
One day, the app worked. The next day I updated to Xcode 11 and now the app crashes with "unexpectedly found nil" on line 27 (when executing line 15) in the picture.
I asked my co-worker who doesn't yet have Xcode 11, and his doesn't crash. we are on the same branch/commit...everything.
Any advice? any way around this?
My code:
// ticket.timeOrdered == "2019-10-03 22:54:57 +0000"
let ticketDate = ticket.timeOrdered.asCrazyDate.timeIntervalSince1970
extension String {
var asCrazyDate: Date {
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss +zzzz"
dateFormatterGet.timeZone = .current
return dateFormatterGet.date(from: self)!
}
}
The date format string is incorrect. The +zzzz is not an acceptable format. See the timezone related sections of the table in Date Format Patterns. The + shouldn’t be there. And zzzz is for long descriptions of the time zone (e.g. “Pacific Daylight Time”). You can verify this by using the same formatter to build a string from Date() and you’ll see that it’s not resulting in the +0000 like you might have expected.
The latest SDK’s date formatter is no longer as forgiving regarding these sorts of format string errors as the previous versions were. But rather than reverting your Xcode version, you really should just fix that date format string. For example, you could use Z instead of +zzzz, which will correctly interpret the +0000 (or whatever) as the time zone portion of the string.
A few other suggestions, if you don’t mind:
You don’t need asCrazyDate in this example. There’s no point in getting a date, using string interpolation to build the string representation, and then using a formatter to convert the string back to a date (which you originally started with). You can just use the Date directly:
func getDate() -> TimeInterval {
return Date().timeIntervalSince1970
}
Date formatters are notoriously computationally intensive to create, and if you’re using this computed property a lot, that can really affect performance. It’s better to instantiate date formatters once, if possible.
If you’re trying to build some invariant date string for some reason, it’s better to use something like ISO8601DateFormatter. So don’t build your date strings using string interpolation, and don’t build your own formatter.
let formatter = ISO8601DateFormatter()
let now = Date()
let string = formatter.string(from: now) // not "\(now)"
let date = formatter.date(from: string)
print(now, string, date)
If you’re stuck with this date format (perhaps you’ve already stored dates using this string format), you can use the custom dateFormat string, if you must. But as Technical Q&A 1480 suggests, you might want to set the locale (and I’d suggest setting the timeZone, too, so your date strings are comparable).
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
I understand that in iOS/Swift creating DateFormatters and setting .dateFormats are expensive, I've read a bunch of different takes here on SO and in blogs, but am still unsure of the best way to efficiently deal with DateFormatter and .dateFormat. Specifically I'm working with an app that roughly mimics Apple's iPhone Weather app UI. Dates arrive via API in timeIntervalSince1970/Unix format. On each view controller I'll use one .dateFormat for the current weather e.g. "EEEE, MMM dd, y", another for formatting date in each of 7 cell in the daily weather table view e.g. "EEEE", and another for the hour in 24 cells in a horizontally scrolling collection view e.g. "ha". Users also page through various view controllers for each location they've saved. I haven't noticed much of a performance hit, but I'd really like to ensure I'm being most efficient, and properly thinking this through (e.g. is Singleton the way to go?).
I had created a Singleton with a single DateFormatter as a static variable, and also created a function to set the .dateFormat and .timeZone for the date formatter (see below). I've assumed based on prior reading that if I stick to iOS 10 or later I don't have to worry about thread safety & that it's likely not a concern in this kind of app, anyway.
class Formatter {
static var dateFormatter = DateFormatter()
private init() {}
static func formatTimeForTimeZone(unixDate: TimeInterval, formatString: String, timeZone: String) -> String {
let usableDate = Date(timeIntervalSince1970: unixDate)
self.dateFormatter.dateFormat = formatString
self.dateFormatter.timeZone = TimeZone(identifier: timeZone)
let dateString = self.dateFormatter.string(from: usableDate)
return dateString
}
}
Each time I'd need to set the date in a view or custom cell class, I'd just call the function, passing in appropriate values like this:
let dateString = Formatter.formatTimeForTimeZone(unixDate: someTime, formatString: someFormatString, timeZone: someTimeZone)
Is it correct that this approach doesn't save me much because I'm setting a .formatString at each call (in each cell)? If so, is there a more sound approach? Advanced thanks for setting me straight.
EDITED FOR BETTER ANSWER:
Thanks Leo for setting me straight & offering suggestions.
After reviewing options & trying out a few things, the solution I ended up using was creating a private global data formatter with class-specific dateFormat String on top of each class that needed a dateFormatter. Being global gives persistence, so I'm not re-creating the DateFormatter or setting .dateFormat String (all allegedy expensive). The global is below, with only change class-to-class being the .dateFormat String
private let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, MMM dd, y"
return dateFormatter
}()
I then created an extension of TimeInterval that handled the formatting that I needed, taking an IANA Time Zone String and DateFormatter (passing in the class's global, when called).
extension TimeInterval {
func format(timeZone: String, dateFormatter: DateFormatter) -> String {
let usableDate = Date(timeIntervalSince1970: self)
dateFormatter.timeZone = TimeZone(identifier: timeZone)
let dateString = dateFormatter.string(from: usableDate)
return dateString
}
}
If anyone's also looking at alternatives, the caching approach offered by mluton/sandmoose, here, is quite nice:
https://gist.github.com/mluton/98ab2b82bd18f7a7f762#file-cacheddateformatter-swift
I'm new to swift and would appreciate some help with string manipulation. I'm trying to get the current date off NSDate and put it into a text field for an app I'm working on. I tried to use NSDateFormatter to put the ios system date into the international form or dd-MM-yyyy, but I just keep getting all these errors and nothing works. I could use the American date format, I just really need it to work. I don't really know swift that much, but I know that other tutorials I tried to follow on stack overflow directed me to put some code in the view controller using NSDate. I worked on some other tutorials and tried to make them do what I needed to and this is the result. It used to create a date and timestamp but I tried to cut the parts out that deal with time. I think I just made it worse.
func convertDateFormatter(date: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
guard let date = dateFormatter.date(from: date) else {
assert(false, "no date from string")
return ""
}
dateFormatter.dateFormat = "dd-MM-yyyy"
let timeStamp = dateFormatter.string(from: date)
return timeStamp
}
My version of swift doesn't recognize NSDate, it wants to change it to just Date, I don't know how it affects how I am supposed to go about doing this. I changed it to just Date in the code and it still doesn't work.
In addition, yesterday my mobile apps teacher and I tried to equate a custom variable and the text field, but it does not work.
var UIDateStamp = UITextField().self
I could be wording my search incorrectly but I have searched this same query all the different ways I could come up with, but every solution I have tried thus far gives me a lot of errors that my coding class and I cannot solve.
I would greatly appreciate help with this issue.
If you need to system date they you need create function without parameter.
Swift 3
func convertDateFormatter() -> String {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy" // change format as per needs
let result = formatter.string(from: date)
return result
}
If you want a date format depending on the current locale use the timeStyle and dateStyle properties.
This code – as computed property – returns M/d/yy for the US locale
var timeStamp : String {
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateStyle = .short
return formatter.string(from: Date())
}
A date style medium returns MMM d, yyyy
how can i return a NSDate in a predefined time zone from a string
let responseString = "2015-8-17 GMT+05:30"
var dFormatter = NSDateFormatter()
dFormatter.dateFormat = "yyyy-M-dd ZZZZ"
var serverTime = dFormatter.dateFromString(responseString)
println("NSDate : \(serverTime!)")
the above code returns the time as
2015-08-16 18:30:00 +0000
The date format has to be assigned to the dateFormat property of the date formatter instead.
let date = NSDate.date()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let str = dateFormatter.stringFromDate(date)
println(str)
This prints the date using the default time zone on the device. Only if you want the output according to a different time zone then you would add for example
Swift 3.*
dateFormatter.timeZone = NSTimeZone(name: "UTC")
Swift 4.*
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
also refer link http://www.brianjcoleman.com/tutorial-nsdate-in-swift/
how can i return a NSDate in a predefined time zone?
You can't.
An instance of NSDate does not carry any information about timezone or calendar. It just simply identifies one point in universal time.
You can interpret this NSDate object in whatever calendar you want. Swift's string interpolation (the last line of your example code) uses an NSDateFormatter that uses UTC (that's the "+0000" in the output).
If you want the NSDate's value as a string in the current user's calendar you have to explicitly set up a date formatter for that.
Swift 4.0
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
If you always have the same time zone for the input string, you can create two date formatters to output the local time zone (or a specified one):
let timeFormatterGet = DateFormatter()
timeFormatterGet.dateFormat = "h:mm a"
timeFormatterGet.timeZone = TimeZone(abbreviation: "PST")
let timeFormatterPrint = DateFormatter()
timeFormatterPrint.dateFormat = "h:mm a"
// timeFormatterPrint.timeZone = TimeZone(abbreviation: "EST") // if you want to specify timezone for output, otherwise leave this line blank and it will default to devices timezone
if let date = timeFormatterGet.date(from: "3:30 PM") {
print(timeFormatterPrint.string(from: date)). // "6:30 PM" if device in EST
} else {
print("There was an error decoding the string")
}
The number 1 means 1 regardless of language. Yet in English it's spelled as one, in Spanish it's una, in Arabic it wahid, etc.
Similarly 123982373 seconds pass 1970 is going to reflect differently in different timezones or calendar formats, but's all still 123982373 seconds passed 1970
The difference between 3 seconds and 7 seconds is 4 seconds. That doesn't require a calendar. Neither you need a calendar/timezone to know the difference in time between these two Epoch times 1585420200 and 1584729000
Dates are just a timeInterval from January 1, 1970 (midnight UTC/GMT). Dates also happen to have a string representation.
Repeating Nikolia's answer, Swift's default string interpolation (2015-08-16 18:30:00 +0000) uses a DateFormatter that uses UTC (that's the "+0000" in the output).
Calendars with the use of timezones give us a contextual representation that is just easier to understand than trying to calculate the difference between two gigantic numbers.
Meaning a single date (think of a single timeInterval since 1970) will have a different string interpretations per calendar. On top of that a calendar will itself vary based on time zones
I highly recommend that you go and play around with this Epoch converter site and see how selecting a different timezone will cause the string representations for the same moment/date/timeInterval to change
I also recommend to see this answer. Mainly this part:
Timezone is just an amendment to the timestamp string, it's not considered by the date formatter.
To consider the time zone you have to set the timeZone of the formatter
dateFormatter.timeZone = TimeZone(secondsFromGMT: -14400)
I'm experiencing a really strange issue that sounds more like a system bug.
I want to format a date using only Hour and Minute information and, if necessary, display AM/PM.
Here is my code:
extension NSDate {
func localizedStringTime()->String {
let dateFormatter = NSDateFormatter()
dateFormatter.locale = NSLocale.currentLocale()
dateFormatter.dateFormat = "HH:mm"
}
}
As you can see i'm using HH and not hh and as stated on Apple documentation it should automatically add AM/PM if user chooses 12h format:
The representation of the time may be 13:00. In iOS, however, if the
user has switched 24-Hour Time to Off, the time may be 1:00 pm.
I found that it works perfectly on my Device (I'm based in Europe) but it doesn't work on USA Devices and on Simulator, where even if user selects 12h format it still returning the 24h format.
I've also tried to change my Region to United State but from my device it still work correctly.
Do you see any problem with my code?
Anyway, this problem is also
You can use the localized string from date function like this...
extension NSDate {
func localizedStringTime()->String {
return NSDateFormatter.localizedStringFromDate(self, dateStyle: NSDateFormatterStyle.NoStyle, timeStyle: NSDateFormatterStyle.ShortStyle)
}
}
Swift 3, 4, 5:
extension Date {
var localizedStringTime: String {
return DateFormatter.localizedString(from: self, dateStyle: .none, timeStyle: .short)
}
}