I can't convert iso8601 to string swift - ios

I have a string coming from API and its format will be like this
"2021-03-01T15:00:00+07:00"
so i try to convert this string to date using this code
// string to date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let date = dateFormatter.date(from: isoDate)!
print("date from date Formatter = \(date)")
// convert date back to string
dateFormatter.dateFormat = "EEEE HH:mm"
let dateString = dateFormatter.string(from: date)
print("date string \(dateString)")
return dateString
The result that I expect is -> "2021-03-01 08:00:00 +0000", "Monday 15:00"
When I try this on playground the result is what I want, but when I try this on my project the result is
-> "1478-03-01 08:00:00 +0000", "Sunday 14:42"
How can I change the result to the same as i expect? Thanks

It looks like you are using a different calendar than you expect in your project (buddhist maybe?) and I guess this is because you haven't set one explicitly so it's the one set in System Preferences.
So if you for some reason do not want to use the users current calendar (and locale and time zone) you need to set those properties on your date formatter instance
//Gregorian calendar
dateFormatter.calendar = Calendar.init(identifier: .gregorian)
//UTC time zone
dateFormatter.timeZone = TimeZone(identifier: "UTC")
//English locale
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
This will give you the expected output.
Note that the playground is a bit inconsequent in what it uses and it seems to be a mix of what we have set in our System preferences and hardcoded values.

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.

why Date will return nil when using correct format in swift 4?

so here is the string Date that I want to Convert
2019-03-22T00:00:00
and here is the problem
Remember that I used "yyyy-MM-dd'T'HH:mm:ss" or "yyyy-MM-ddTHH:mm:ss" or "yyyy-MM-dd HH:mm:ss" or yyyy-MM-dd'T'HH:mm:ssZ and some other similar formats
You have to set a time zone and locale when parsing internet dates:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
// necessary to avoid daylight saving (and other time shift) problems
dateFormatter.timeZone = TimeZone(secondsFromGMT: TimeZone.current.secondsFromGMT())
// necessary to avoid problems with 12h vs 24h time formatting
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
print(dateFormatter.date(from: "2019-03-22T00:00:00"))
The default time zone contains information about daylight saving time and some specific times does not exist there. Instead, we have to use a generic time zone.
See https://developer.apple.com/library/archive/qa/qa1480/_index.html
22 March 2019 around midnight is the day & time when daylight saving change in Iran makes an hour non-existent.
The same can be achieved using ISO8601DateFormatter:
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = ISO8601DateFormatter.Options.withInternetDateTime.subtracting(.withTimeZone)
print(dateFormatter.date(from: "2019-03-22T00:00:00"))
Try this function might help
public func dateFormatter(strDate: String) -> String{
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "fa_IR")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = dateFormatter.date(from: strDate)
let dateString = dateFormatter.string(from: date!)
return dateString
}
dateFormatter.isLenient = true
Ignores missing hours during daylight saving time offset

Date() returns hour over 24

I have to send date from my iPhone through my app to server, but I was surprised that the date had stored as NULL (not always) in server, that's the code used for that:
timeS = (Date().millisecondsSince1970)
// make some assertions on timeS
let date = Date(timeIntervalSince1970: timeS)
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateString = dateFormatter.string(from: date)
return dateString
It seems like the c# code in the server side tried to convert the string to date and it failed, so it was stored NULL.
So recently I tried to send the date without any modifications in a string to see what happened:
self.backS.sendLocation(msg: "data fitched \(Date())")
and that's what I found in the database:
data fitched 2018-05-08 77:45:44 AM +0000
it must be:
data fitched 2018-05-08 07:45:44 AM +0000 (because that was the time for the server)
I don't know why is that happened!!
I really appreciate anybody's help, thanks in advance.
Try to get your current date and time like this in Swift.
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let result = formatter.string(from: date)
self.backS.sendLocation(msg: "data fetched \(result)")
Your result will be like this.
"2018-05-12 14:34:51 +0000"
To send the date to server you should rather use Unix timestamp, instead of date in string format.
But, if you insist on a string, you should use DateFormatter. When using DateFormatter you should always set the locale property of your formatter. That ensures the formateer will return consistens results, regardless of your phone's regional settings.
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let dateString = formatter.string(from: Date())
It's important to set the locale property before you set the dateFormat property.
If you don't set the locale property, the formatter might return nil when working with 24-format on devices with 12-hour format set, and vice versa.
Source: Technical Q&A QA1480

How to save datetime with timezone in iOS?

I am new to iOS development, being an Android developer I am used to use have an object that saves a datetime with a given timezone (from Joda-Time library).
After reading the iOS documentation about dates and times (https://developer.apple.com/documentation/foundation/dates_and_times) I still have doubts about which class should I use to save datetimes. Given the Date/NSDate class description "A specific point in time, independent of any calendar or time zone." it seems very useless because it is timezone independent and time without a timezone does not make any sense, since it does not have any context.
My real problem (TL;DR):
I have a database where date times are stored in UTC like this "yyyy-MM-dd hh:mm:ss". I would like to init an object with some kind of DateFormatter (string with this format "yyyy-MM-dd hh:mm:ss") plus a timezone (UTC) to easily convert to any Timezone that I want (to show to the user on his default timezone time). How can I accomplish this in iOS?
Edit: Imagine I have a class Event with a title and a start time. Title is a String, what start time should be?
You use a DateFormatter for this.
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
The formatter.locale sets the current locale for the user and formatter.dateFormat sets the desired date format. In your case yyyy-MM-dd HH:mm:ss.
To call it simply:
let utcDateFromServer = "2017-01-01 22:10:10"
let date = formatter.date(from: utcDateFromServer)
A Date is a point in time, as mentioned in other comments & in the documentation.
If you want to convert the UTC time into local time, you'll need to first convert the String "yyyy-MM-dd hh:mm:ss" from your database into a Date using DateFormatter.
let dateStringUTC = "2018-01-01 00:00:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
//Set the input timezone (if you don't set anything, the default is user's local time)
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let date : Date = dateFormatter.date(from: dateStringUTC)!
Then convert the Date back into String using DateFormatter with the respective TimeZone
let outputDateFormatter = DateFormatter()
outputDateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
//Set the output timezone (if you don't set anything, the default is user's local time)
//outputDateFormatter.timeZone = someTimeZone
let dateString = outputDateFormatter.string(from: date)
print(dateString)
Output: 2017-12-31 17:00:00
And you can just change the input and output timezone to do the opposite.

How to change the timeZone in SwiftDate?

I have displayed the list of timezones in my app. If user selects a particular timezones, I need to change the local timezone to the selected timezone by the user.
let region = Region(tz: timeZoneName.timeZone , cal: cal, loc: cal.locale!)
let date = Date().inRegion(region: region).absoluteDate
Here is the problem, the region is changed to the selected timezone but the date issuing the local timezone.
A Date contains no timezone. From apple's docs: A specific point in time, independent of any calendar or time zone.
The timezone comes into play as soon as you want to present a date to the user. And that's what a DateFormatter is for. As #AlexWoe89 already pointed out, it let's you convert a string, containing a date into a Date object, but also lets you convert a given date into a string representing the date in the time zone you set to the timeZone property of DateFormatter.
let date = Date()
var dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
dateFormatter.timeZone = TimeZone(identifier: "America/Los_Angeles")
let dateString1 = dateFormatter.string(from: date)
dateFormatter.timeZone = TimeZone(identifier: "Germany/Berlin")
let dateString2 = dateFormatter.string(from: date)
This will store 2017-10-23 04:27 in dateString1, while the same date leads to 2017-10-23 13:27 in dateString2.
You can use DateFormatter as a solution, try something like this:
let dateString = "<yourDateAsString>"
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // => there are a lot of identifiers you can use
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
dateFormatter.defaultDate = Date()
dateFormatter.dateFormat = "dd-MM-yyyy hh:mm” // => your needed time format
let convertedDate = dateFormatter.date(from: dateString)

Resources