Timezone in swift [duplicate] - ios

This question already has answers here:
How can I parse / create a date time stamp formatted with fractional seconds UTC timezone (ISO 8601, RFC 3339) in Swift?
(13 answers)
Swift - Get local date and time
(11 answers)
Closed 12 months ago.
I am struggling quite a bit with dates. I have the following code:
Current Date in Amsterdam: 22-Februari-2022 - 11:40
Current Date in New York: 22-Februari-2022 - 05:40
The dateBoughtString goes in as follows: 2022-02-18T19:50:47.081Z
The current date is just the current date.
let dateFormatterNew = DateFormatter()
dateFormatterNew.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
dateFormatterNew.timeZone = TimeZone(abbreviation: "GMT+1:00")
dateFormatterNew.locale = Locale(identifier: "nl-NL")
let dateBoughtTemp = dateFormatterNew.date(from: positionStatsString[0])!
print(dateBoughtTemp) // Prints: 2022-02-18 18:50:47 +0000
dateFormatterNew.timeZone = TimeZone(abbreviation: "GMT-5:00")
dateFormatterNew.locale = Locale(identifier: "en_US")
let dateNowTemp = dateFormatterNew.string(from: Date())
let dateBoughtTempTwo = dateFormatterNew.string(from: dateBoughtTemp)
print(dateNowTemp) // Prints: 2022-02-22T05:41:49.973Z
print(dateBoughtTempTwo) // Prints: 2022-02-18T13:50:47.081Z
let dateNow = dateFormatterNew.date(from: dateNowTemp)
let dateBought = dateFormatterNew.date(from: dateBoughtTempTwo)
print(dateNow!) // Prints: 2022-02-22 10:41:49 +0000 **INCORRECT**
print(dateBought!) // Prints: 2022-02-18 18:50:47 +0000 **INCORRECT**
When I convert to string all seems fine and it works as it should.
But when I convert those strings back to a date they just go back to Amsterdam time with the current date even being one hour off.
What am I missing here?

The problem is in your's parameter 'Z':
'' means that it's content doesn't involved in time formatting.
So when you apply timeZone parameter date is printed in particular time zone without correct timeZone suffix and when it's scanned it's scanned in particular time zone, just expecting that there will by Z character at the end. So when you are formatting to date and then to string you are accumulating error caused by timezone difference.
Correct format will be "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" or better to use ISO8601DateFormatter because you can't set invalid format in it.
So your printed dates will have valid timezone suffix and timezone suffix will be considered in backward conversion.
Another moment: you shouldn't convert string back to date with localized formatter, if it's UI part, but for that you can use UIDatePicker instead of UITextField.
So full code will be:
let isoDateFormatter = ISO8601DateFormatter()
isoDateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let date = isoDateFormatter.date(from: "2022-02-18T19:50:47.081Z")!
let now = Date()
do {
let amsterdamDateFormatter = DateFormatter()
amsterdamDateFormatter.timeZone = .init(abbreviation: "GMT+1:00")
amsterdamDateFormatter.dateStyle = .long
amsterdamDateFormatter.timeStyle = .short
print("now in Amsterdam: \(amsterdamDateFormatter.string(from: now))")
print("time in Amsterdam: \(amsterdamDateFormatter.string(from: date))")
}
do {
let newYourkDateFormatter = DateFormatter()
newYourkDateFormatter.timeZone = .init(abbreviation: "GMT-5:00")
newYourkDateFormatter.dateStyle = .long
newYourkDateFormatter.timeStyle = .short
print("now in NY: \(newYourkDateFormatter.string(from: now))")
print("time in NY: \(newYourkDateFormatter.string(from: date))")
}

Use the below code for formatter, Change the timezone and dateFormat according to your need:
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"

Related

Swift date format returning wrong date

I need to convert my date to string and then string to date. date is "2020-10-17 1:22:01 PM +0000"
Here is my date to string conversion code:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
let string = formatter.string(from: "2020-10-17 1:22:01 PM +0000")
let createdAT = string
its returning "2020-10-17 18:51:30+05:30"
Here is my string to date conversion code:
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ssZ"
let date = dateFormatter.date(from:date)!
its returning "2020-10-17 1:21:30 PM +0000 - timeIntervalSinceReferenceDate : 624633690.0"
its returning the wrong date after i convert string to date. i need "2020-10-17 18:51:30+05:30" this time to be return when i convert string to date.....
The code in your question is muddled up. You try to convert a string into a string in the first example and something unspecified into a Date in the second example.
Here's how to convert a Date into a String:
import Foundation
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
let string: String = formatter.string(from: Date())
print(string) // prints for example 2020-10-18T10:54:07+01:00
Here's how to convert a string into a date
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ssZ"
let date: Date = formatter.date(from: "2020-10-18 10:59:56+0100")! // In real life, handle the optional properly
print(date) // prints 2020-10-18 09:59:56 +0000
When you print a Date directly, it automatically uses UTC as the time zone. This is why it changed it in the code above.
In the examples, I explicitly specified the type of string and date to show what type they are. Type inference means you can omit these in normal code.
As a general rule when handling dates:
always use Date in your code. Date is a type that stores the number of seconds since Jan 1st 1970 UTC.
Only convert dates to strings when displaying them to the user or communicating with an external system.
When calculating periods etc, always use a Calendar to get things like date components and intervals in units other than seconds. You might think to get "the same time tomorrow" you could just add 24 * 60 * 60 to a Date but in many countries, like mine, that will work on only 363 days in the year. Calendar will correctly handle things like daylight saving and leap years.

Wrong date in swift 5 after conversion [duplicate]

This question already has answers here:
How can I parse / create a date time stamp formatted with fractional seconds UTC timezone (ISO 8601, RFC 3339) in Swift?
(13 answers)
Closed 3 years ago.
I am converting current date into GMT/UTC date string. But every time it returns me with wrong date.
My todays date is 07 February 2020, 11:09:20 AM. You can refer below image.
Here is my code :
let apiFormatter = DateFormatter()
//apiFormatter.dateStyle = DateFormatter.Style.long
//apiFormatter.timeStyle = DateFormatter.Style.long
//apiFormatter.calendar = Calendar.current
apiFormatter.timeZone = TimeZone.init(identifier: "GMT") //TimeZone(abbreviation: "UTC") //TimeZone.current //
//apiFormatter.locale = Locale.current
//apiFormatter.dateFormat = "yyyy-MM-DD HH:mm:ss"
apiFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
//apiFormatter.dateFormat = "yyyy-MM-dd'T'hh:mm:ssZ"
let endDate = apiFormatter.string(from: Date())
print(endDate)
And what I am getting in return is also you can check in image - 2020-02-38T05:33:34.598Z. I have tried with all the format, but no any luck. Can anyone suggest where it is going wrong?
First of all, the format should be:
apiFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
The Z is not a literal letter, it's the description of the time zone. However, making it a literal won't probably make a problem.
The 38 for day from your output is obviously caused by the DD format you have commented out.
Nevertheless, you have to set the locale:
apiFormatter.locale = Locale(identifier: "en_US_POSIX")
Otherwise you will have problems with 12/24h switching.
let apiFormatter = DateFormatter()
apiFormatter.locale = Locale(identifier: "en_US_POSIX")
// remove this if you want to keep your current timezone (shouldn't really matter, the time is the same)
apiFormatter.timeZone = TimeZone(secondsFromGMT: 0)
apiFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let endDate = apiFormatter.string(from: Date())
print(endDate) // 2020-02-07T08:25:23.470+0000
print(Date()) // 2020-02-07 08:25:23 +0000
Also note that you can use ISO8601DateFormatter instead of DateFormatter.
Try this and adjust according to what format you are getting from server -
private func getFormatedDateInString(_ dateString: String) -> String? {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
dateFormatter.timeZone = TimeZone(identifier: "UTC")
if let date = dateFormatter.date(from: dateString) {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
dateFormatter.timeZone = TimeZone.current
let timeStamp = dateFormatter.string(from: date)
return timeStamp
}
return nil
}

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.

dateFromString() returning wrong date swift 3.0

i am passing "01/12/2017" in the fromDate.text(textfield), but receiving unexpected output.
let formatter = DateFormatter.init()
formatter.dateFormat = "dd/mm/yyyy"
startDate = formatter.date(from: fromDate.text!)
print("startDate = \(startDate)")
output is : 31/12/2016
The format of date should be dd/MM/yyyy not dd/mm/yyyy. The mm indicates the minutes and MM indicates the month.
And also add the below line in your code
formatter.timeZone = TimeZone(abbreviation: "GMT+0:00")
This line of code set time zone. If you not, then you get 30/11/2017 in output.
The reason behind this is when string date not contain time then formatter assume that it is midnight and you also not given the timezone so it will take current timezone.
It has to be dd/MM/yyyy dateformat. MM in capital.
func convertToString(of dateTo: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy" //Your New Date format as per requirement change it own
let newDate: String = dateFormatter.string(from: dateTo) //pass Date here
print(newDate) //New formatted Date string
return newDate
}

Converting string EST time to localized time

Assume I'm given an input string like "7:00". I know that the time is EST but I want to convert it to localized time (so for people in PST it says 4:00). I tried the following code, but I keep getting a 24-hour time from it and the complete date (e.g: Optional(2000-01-01 16:00:00 +0000)). I want just the hour:min time...what am I doing wrong?
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
gameClock = "\(dateFormatter.dateFromString("7:30"))"
println(gameClock)
You need to specify the time zone for your date formatter that you use to process the server date. By default, the time zone of the date formatter is the default time zone of the device.
So, for your above code, you should do this:
let dateStringFromServer = "07:00"
let serverDateFormatter = NSDateFormatter()
serverDateFormatter.dateFormat = "HH:mm"
serverDateFormatter.timeZone = NSTimeZone(name: "EST")
let dateFromServer = serverDateFormatter.dateFromString(dateStringFromServer)
let localDateFormatter = NSDateFormatter()
serverDateFormatter.dateFormat = "HH:mm"
let localizedStringForThatDate = localDateFormatter.stringFromDate(dateFromServer!)
The dateFormatter.dateFromString("7:30") gives you an NSDate not an NSString. You have make use of another NSDateFormatter object to get the date in desired string format.
let dateFormatterForDisplay = NSDateFormatter();
dateFormatterForDisplay.dateFormat = "HH:mm"
dateFormatterForDisplay.locale = NSLocale(localeIdentifier: "en_US_POSIX")
(you need the last line in the above if you want to force 12 hour mode, the default is NSLocale.currentLocale() and can be 12 hr or 24 hr mode for different locales)
Then
let displayString = dateFormatterForDisplay.stringFromDate(gameClock)

Resources