Swift dateFormat is returning wrong time - ios

I want to return a date/time coming from my API. Server is based in Amsterdam.
Swift:
//load from api
print(date)
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
df.timeZone = TimeZone(identifier: "Europe/Amsterdam")
df.locale = Locale(identifier: "en_US")
var updated = dateFormatter.date(from: "\(date)")
//return format
print(updated)
Return:
2020-01-10 00:01:01
Optional(2020-01-09 23:01:01 +0000)
I don't know why, but the output is returning GMT I think. To output should be 2020-01-10 00:01:01 +0000 because Amsterdam is GMT+1.

As vadian explained in the comments it's working as it supposed to.
However, if you want to display a local date instead of UTC, you can use my snippet here.
If know you will always want just one given time zone just modify the code to add the correct amount of seconds.
(In the case of Amsterdam time it's +3600 seconds to UTC for example.)

Related

Dateformatter Result

Here is my DateFormatter Code
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
let dateString = formatter.string(from: date)
Now:
po dateString
gives the result "May 18"
po date gives the result 2021-05-17 18:30:00 +0000
This does not make sense to me - why is the Date May 17 2021 being converted to the String May 18?
A Date object records an instant in time anywhere on the planet.
That instant in time will be on a different calendar day (day/month/year) depending on what time zone you are in.
By default, date formatters work in the device's current time zone.
When you log a Date using po date or print(date) the default description of a Date displays that date in UTC using the ISO 8601 date format. Depending on the user's time zone, the Date in UTC might be on a different calendar day than it is in the local time zone
If you want to log a date in the user's local time zone, use po date.description(with: Locale.current) or po DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .medium)
This depends on the current time zone of the formatter used , for 0 based use
formatter.timeZone = TimeZone(abbreviation: "UTC")

String to date with UTC timezone

I am struggling with Date and I'm assuming is TimeZone.
Currently I get from my backend a string like this "2020-04-07" and when I try to convert it to date it turns into 2020-04-06 22:00:00 +0000. I am in Spain (UTC+2) which I guess this is why it removes 2 hours?
This is my date formatter:
var dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.timeZone = TimeZone.current
return dateFormatter
}()
And I call it dateFormatter.date(from: startDateString)
I am setting my current timezone but seems to be ignoring it or am I missing something?
I have followed a lot of answers from here but it's always the same result.
Thank you
The Date object does not have any inherent locale / time zone. It just represents a moment in time. If you want to see that Date as a string in a specific locale/time zone you have to use a date formatter. Or there's descriptionWithLocale. If you use print it will print a debug description of the Date instance in UTC.

Preventing Date from being localized

I have the following string:
let dateString = "2018-04-18T04:54:00-04:00"
I initialize a Date via the ISO8601DateForamtter by doing the following:
let formatter = ISO8601DateFormatter()
let date = formatter.date(from: dateString)
If I print the date, I get the following:
Apr 18, 2018 at 1:54am
The formatter is automatically converting the time into my local time. How can I prevent accounting for my time zone? For example, I want the Date object to show the following instead:
Apr 18, 2018 at 4:54am
With ISO8601, 2018-04-18T04:54:00-04:00 means 2018-04-18 04:54:00 in GMT -4h. To print the time as it is in the original string, you need to create a date formatter with the specific time zone which is -4.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dateFormatter.string(from: date))
You will get
2018-04-17 04:54:00
FYI, I'm adding a link for ISO8601
You need to parse the timezone from your date string and use it to set the timezone from your date formatter:
func secondsFromGMT(from string: String) -> Int {
guard !string.hasSuffix("Z") else { return 0 }
let timeZone = string.suffix(6)
let comps = timeZone.components(separatedBy: ":")
guard let hours = comps.first,
let minutes = comps.last,
let hr = Int(hours),
let min = Int(minutes) else { return 0 }
return hr * 3600 + min * 60
}
let dateString = "2018-04-18T04:54:00-04:00"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
formatter.locale = Locale(identifier: "en_US_POSIX")
if let dateFromString = formatter.date(from: dateString) {
formatter.timeZone = TimeZone(secondsFromGMT: secondsFromGMT(from: dateString))
formatter.dateFormat = "MMM dd, yyyy 'at' h:mma"
formatter.amSymbol = "am"
formatter.pmSymbol = "pm"
print(formatter.string(from: dateFromString)) // Apr 18, 2018 at 4:54am
}
Instead of logging the Date directly, have a look at the string(from:timeZone:formatOptions:) method on ISO8601DateFormatter. With this, you should be able to get a date string for any time zone you desire.
You should set your formatter to the appropriate timezone such as (UTC example below):
formatter.timeZone = TimeZone(identifier: "UTC")
or alternatively specify against GMT:
formatter.timeZone = TimeZone(secondsFromGMT: 0)
The date that you are receiving from your current formatter is technically correct. Setting the date backwards as described in the currently accepted answer is not advised because you are effectively hard-coding an intended time zone. As soon as your device enters another time zone (or if a user downloads your app outside of the current time zone), your information will be incorrect.
If you are trying to display this time in the UTC time zone, you need to use another formatter to correctly format the output in the target time zone.
let utcFormatter = DateFormatter()
utcFormatter.timeZone = TimeZone(secondsFromGMT: 0)
// Perform any other transformations you'd like
let output = utcFormatter.string(from: date)
But why is your original date correct?
The Date API is incredibly robust and doing a lot of things under-the-hood, but is effectively implemented using a simple Double. The automaic time-zone information that it's displaying to you is an abstraction to make it easier to reason about. A date technically has no knowledge of what time zone it's in – but converting it to a string implicitly applies an inferred date formatter on the date and returns information it thinks will be most useful to you.
If you're doing manipulations on a date, you're likely using the Calendar API. You typically get a new instance from using Calendar.current, which will create a new calendar with your current time zone information. You can change the represented time zone of the calendar like this:
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 0)
This will give you relative dates that will work in any time zone without modifying the base Date object that you're working with.

Convert 12 hour date format into 24 hour format & vice versa in swift?

I have a date from a UIControl. I get date as string such as 06-12-2016 01:25 PM. Now I want to convert it into a format as 2016-12-06 13:25:00 I have tried below code t do so but it just gives me wrong date for ex. 2016-12-06 18:55:00. I have used below code for this:
func converStingToDate(str_date:String)->String
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy H:mm a"
let date = dateFormatter.date(from: str_date)
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter.string(from: date!)
}
Please suggest me a better code. Also if you guys can help me to understand all this date formatting concept how it is done ?
The code is correct and the date is correct (although you should use h for 12-hour hour).
Your time zone is UTC+0530 and print() displays the date in UTC.
If you need to print the correct local time set the time zone of the formatter to UTC:
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")`

Converting time from GMT in iOS behaving oddly (Reverse)

So I am trying to convert some times I receive in UTC/GMT to the local time zone using the following code:
let gmtFormatter: NSDateFormatter = NSDateFormatter()
gmtFormatter.timeZone = NSTimeZone(name: "GMT")
gmtFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let gmtDate: NSDate = gmtFormatter.dateFromString(dateString)!
print("GMT \(gmtDate)")
let localFormatter: NSDateFormatter = NSDateFormatter()
localFormatter.timeZone = NSTimeZone.localTimeZone()
localFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let localDate: NSDate = localFormatter.dateFromString(gmtFormatter.stringFromDate(gmtDate))!
print("Local \(localDate)")
However, I find that the conversion is working in revers. PST is UTC-8 and the outputs are 8 hours ahead instead of 8 hours behind. So I tested with a London Time Zone (CET) which is UTC+1 and should be 1 hour ahead of GMT. The results was a time that was 1 hour behind.
This was the string I am testing with: "2016-02-24 00:05:54"
For PST this should convert to 2016-02-23 04:05:54, but instead converts to 2016-02-24 08:05:54 (notice the way it should be is on the 23rd and not the 24th)
For CET this should convert to 2016-02-24 01:05:54, but instead converts to 2016-02-23 23:05:54 (notice the way it should be is on the 24th and not the 23rd)
Am I missing something glaring?
The problem is that in the second part of your code...
let localDate: NSDate =
localFormatter.dateFromString(gmtFormatter.stringFromDate(gmtDate))!
... you're doing the opposite of what you want. An NSDate is absolute. Its string representation is what is volatile, as it were. So having turned a string into a date with your first formatter, what you now want to do is turn that date into a string with your second formatter.
For example, I'm in California. Now, watch this:
let dateString = "2016-02-24 10:09:08"
let gmtFormatter: NSDateFormatter = NSDateFormatter()
gmtFormatter.timeZone = NSTimeZone(name: "GMT")
gmtFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let gmtDate: NSDate = gmtFormatter.dateFromString(dateString)!
print("GMT \(gmtDate)")
let localFormatter: NSDateFormatter = NSDateFormatter()
localFormatter.timeZone = NSTimeZone.localTimeZone()
localFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let localDateString = localFormatter.stringFromDate(gmtDate) // <-- !!!
print("Local \(localDateString)")
Result:
GMT 2016-02-24 10:09:08 +0000
Local 2016-02-24 02:09:08
That's correct. When it's 10 AM in London, it's 2 AM here.
To put it another way, if you think what you're doing is converting a date from one time zone to another, you have not understood what a date is. It is a date-time and a time zone. There is nothing to convert: it is completely determined, and it is correct no matter where you are, because it says what time zone it is.
Perhaps it would be a bit clearer to you if I changed the next-to-last line format to this:
localFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
The final output is then:
Local 2016-02-24 02:09:08 -0800
which is obviously correct.

Resources