Swift: Get correct time zone from Date Picker? - swift

I am trying to get the correct time zone from the date picker in swift using time formatter, it's not working. I'm getting UTC, not EST.
1) If I print dateFormatter.stringFromDate(datePicker) I get EST, but
2) I don't need a string, I need an NSDate in EST so
3) I can use it to get the timeIntervalSinceDate(NSDate) in EST.
My trick of trying to take it from string back to NSDate as seen below didn't work. It's still in UTC and the time interval since date is not right.
dateFormatter.locale = NSLocale.currentLocale()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date: NSDate = dateFormatter.dateFromString(dateFormatter.stringFromDate(datePicker))!
print(date)
print(date.timeIntervalSinceDate(datePicker))

The above answer is totally wrong. Date picker report the date in system locale anytime, so, if the datePicker shows an 08:00 Time and you are GMT+2, the property date of the picker will be 06:00.
So for have the absolute value of the datePicker you have to pass to him the UTC time zone in view did load with:
datePicker.timeZone = TimeZone.init(identifier: "UTC")
Now, the date property of the picker will be the expected and choosen one.

You cannot "get a time zone" from a date picker. You can just get a date. The date will be independent on the current time zone of the device.
Perhaps you think you have a different date, but actually, there is no such thing as a "UTC date" or "EST date". Instead, there is only one date, and you use date formatters to display them for various time zones.
Note that there is quite a bit of redundancy in your code. The default locale and time zone of a date formatter are already the same values that you set. Also, when you have a method that returns a NSDate you do not have annotate the constant with : NSDate, making your code more verbose and cluttered.
Note that if you print a date the console will always show UTC. e.g.
let date = NSDate() // Nov 10, 9:44 PM
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd hh:mm a"
let dateString = dateFormatter.stringFromDate(date) // "2015-11-10 09:44 PM"
print(date) // "2015-11-10 20:44:54 +0000\n"

To set the TimeZone of a DatePicker to UTC use:
datePicker.timeZone = TimeZone.init(identifier: "UTC")
Notice the camelcase notation of "timeZone".
Unfortunately I don't have enough credit to comment on the last post, which has it almost right, so I had to create a new answer.
A little trivia: TimeZone has been around since iOS 2.0 as is stated here:
https://developer.apple.com/documentation/uikit/uidatepicker/1615976-timezone

Those who are trying to find a solution and are not able to wrap their head around the exact issue. Here is what something I tried:
Use time.addTimeInterval(-14400) function, where -14400 is the 4 hours difference. So if you want UTC to EST do this.
Something like this:-
var time = Date() // assuming you have this in UTC
time.addTimeInterval(-14400)
You can also use addingTimeInterval function which returns you the new date.
I know this is a little wonky cause we're manually doing this, but hope it helps someone.

Related

Converting string to date with a different timezone gives the wrong date

So, this:
import Foundation
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd"
df.timeZone = TimeZone(identifier: "Australia/Currie")!
let todayString = (df.string(from: Date()))
print(todayString)
let today = df.date(from: todayString)!
print(today)
Prints:
2021-02-19
2021-02-18 13:00:00 +0000
For reference, today’s date based on my current timezone is the 18th. So it correctly prints the 19th when using an Australian timezone that moves the time ahead.
What I don't understand is why today is a day behind todayString, since it's constructed from the same DateFormatter. Ideally, they should both print the 19th, right?
Basically, what I'm trying to do is have both a date (from a different timezone) and its string representation.
As mentioned in the comments print displays Date instances always in UTC(+0000). For example check
let date = Date()
print(date)
To print the date in your current locale (and time zone) write
print(today.description(with: .current))

TimeZone Date Formatting Issue

I'm trying to get a date from a string with multiple time zones, It works without any problems if the API returns zones with abbreviations like CST or UTC but it fails if it returns EET
let timeString = "17:32 (EET)"
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm (zzz)"
let time = formatter.date(from: timeString) // return nil
Any idea what is the issue might be?!
Probably because there is more than one timezone that matches the timezone abbreviation or the date formatter's default date (January 1st) doesn't match the daylight savings of the timezone abbreviation used. Not all countries uses daylight savings time as well it might change at any time. Check this link. This will probably happen for all non US timezones abbreviation as well. For example CST it is used for "China Standard Time" and "Chicago Standard Time" as well. You can workaround that issue setting your date formatter property isLenient to true. Note that this will result in a date of January 1st 2000 with a probably incorrect timezone offset. If you have control of your string input you should use the timezone identifiers instead of its abbreviations to avoid ambiguity. You should also set your date formatter's locale to "en_US_POSIX" when parsing fixed date format to avoid date formatter's reflecting the users device locale and settings:
let timeString = "17:32 (EET)"
let formatter = DateFormatter()
formatter.locale = .init(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm (zzz)"
formatter.isLenient = true
let date = formatter.date(from: timeString) // "Jan 1, 2000 at 1:32 PM" "BRT Brazilian Standard Time"
So you should use "GMT+2" or "GMT-3" to avoid ambiguity or as I have already suggested use its identifiers "HH:mm (VV)" i.e. "Europe/Athens" or "America/Sao_Paulo"

Comparing dates in Swift ignoring current timezone

In Swift I can find the difference between two dates using Calendar.current.dateComponents
The problem is that this gives the difference relative to my current time zone.
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm XXX"
let from = zoneFormatter.date(from: "2018-03-25 00:00 +00:00")!
let to = zoneFormatter.date(from: "2018-03-26 00:00 +00:00")!
let components = Calendar.current.dateComponents([.month, .day, .hour, .minute], from: from, to: to)
// components is 1 day, 1 hour, because I am in UK and Daylight Savings started on 25 March.
How can I retrieve the absolute difference between the two instants specified in the same format which dateComponents returns?
Incorrect Date Format String
Looks like your using the incorrect format for your dates. The date format for the specified date of "2018-03-25 00:00 +00:00" is actually "yyyy-MM-dd HH:mm ZZZZZ". Give that format a try and it should fix the problem.
Why does the incorrect format not work for comparison between time zones?
This is because it was the timeZone component of your format string that is incorrect. This means the timeZone will not be accounted for in the comparison.
The timezone of Calendar.current can be changed by setting the system default timezone as follows:
NSTimeZone.default = NSTimeZone(abbreviation: "UTC")! as TimeZone

How to convert time between timezones taking daylightsavings into account?

I have the following function for converting time:
static func convert(date: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "h:m:s a"
formatter.timeZone = TimeZone(identifier: "UTC")
let convertedDate = formatter.date(from: date)
formatter.timeZone = NSTimeZone.local
return formatter.string(from: convertedDate!)
}
Since I'm setting the new time zone based on the device's time zone I taught that daylight savings will be taken into account. But when I passed in 2:00:00 PM it returned 3:0:0 pm instead of 4.
Am I missing something, is there an automatic way to correctly convert time between time zones?
Dealing with daylight saving time only makes sense when the date is known. You don't have a date, just a time. So convertedDate will be January 1, 2001. So whatever the daylight saving rule is for the user's timezone and locale on that date will be used when converting the time.
If you want the time to be treated as "today" then you can set the date formatter's defaultDate.
formatter.defaultDate = Date()
If you want some other specific date, create a Date as needed and use that to set the defaultDate.

How to check time is in which timezone in swift?

I am displaying the current time using this code:
let UTCDate = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm a"
formatter.timeZone = TimeZone(identifier:"GMT")
let defaultTimeZoneStr = formatter.string(from: UTCDate)
Can someone help me to check if this time is in which timezone(eg:central timezone, eastern timezone)....
Unless the time is specified as a string with some kind of time zone indicator, such as "2017-04-14 10:00:00 EDT" or "2017-04-14 10:00:00 -0400", there's no way to tell what time zone for a given time value.
The Swift way to store times as Date values, which simply specify a number of seconds before or after January 1, 2001, UTC, and to display any time value using the calendar and time zone that makes the most sense for the user. Usually, this means using the time zone settings, because in most cases -- but not all cases -- that time zone setting will match the time zone where the user is.
If you want the abbreviated name of the user's current time zone setting, use this:
Calendar.current.timeZone.abbreviation()! // returns "EDT" for me;
// I’m in the eastern time zone
// and on daylight saving time
Or if you prefer getting the time zone by geographic identifier, use this:
Calendar.current.timeZone.identifier // returns "America/New_York" for me
Or if you want the full name of the time zone, try this (and play with the parameters):
// Returns "Eastern Standard Time" for me
Calendar.current.timeZone.localizedName(for: .standard, locale: Locale.current)