Swift: How to parse date & time string with timezone abbreviation? - swift

I receive a date & time string from a server with an information about a timezone formatted in an unusual way:
2017-05-05T12:24:16.286462Z[UTC]
I would like to use DateFormatter to parse it, but I cannot figure out what date format should I use.
I tried parsing it with "yyyy-MM-dd'T'HH:mm:ss.SSSZ'['R']'" or something quite similar but with no luck.
Here is my code:
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en-US")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ'['R']'"
let date = dateFormatter.date(from: date)
What is the right date format for this string?

OK guys, I figured it out.
The right format is
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z['zzz']'"
zzz is for a timezone abbreviation, while Z[ and ] are treated as a plain text.

An alternative approach with ISO8601DateFormatter. The [UTC] portion is stripped with Regular Expression
let dateString = "2017-05-05T12:24:16.286462Z[UTC]"
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let date = formatter.date(from: dateString.replacingOccurrences(of: "\\[^\\[+\\]", with: "", options: .regularExpression))

Related

Swift ISO8601DateFormatter TimeZone

I receive a string timestamp from JSON formatted as follows: "2022-10-06T19:10:00.000Z"
I want to convert the string to my local timezone and possibly even reformat the whole thing to something more readable like "Oct 6 2022, 3:30:45 PM". I assume that I need to use both ISO8601DateFormatter() and DateFormatter() to make that happen?
When I run the code below I get "2022-10-07 02:10:00 +0000" which is tomorrow's date so obviously not my timezone.
How do I properly format this date/time stamp to my current timezone? Thanks!
let jsonDateString = "2022-10-06T19:10:00.000Z"
let dateFormatter = ISO8601DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime, .withDashSeparatorInDate, .withColonSeparatorInTime]
let formattedDate = dateFormatter.date(from: jsonDateString)
print(formattedDate!)

I want to format a date into a string, but its giving me nil, i want to use the date in a UITextField.text

// the creationdate is coming from an api call
var creationDate = "2020-11-04T16:46:59.439212Z"
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
formatter.dateFormat = "dd-MM-yyyy"
var creationDateFormattedInToDate = formatter.date(from:
creationDate)
print("date \(creationDateFormattedInToDate)")
So i want that date in the format 04-11-2020 and pass in a UITextField.text
You will need two formatters, one to parse the input date to a Date object and one to convert the date object to a string of the right format.
The input date seems to be a variant of a internet date/time so we use a ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
The second formatter is a basic DateFormatter with a custom format
let outputFormatter = DateFormatter()
outputFormatter.dateFormat = "dd-MM-yyyy"
And then we can use them like this
if let date = formatter.date(from: creationDate) {
someTextField.text = outputFormatter.string(from: date)
}
You will want to use one formatter for parsing the response from the server (which is in what’s called and “ISO 8601” or “RFC 3339” format), and another for preparing the string representation of the date in the UI.
Regarding the date formatter for parsing the server response:
Set the formatter’s locale to Locale(identifier: "en_US_POSIX").
The setting of the styles when parsing this date string are irrelevant if you’re going to set dateFormat.
When parsing the date from the string, set dateFormat to yyyy-MM-dd'T'HH:mm:ss.SSSSSSX.
If you ever plan on using this formatter for the reverse date-to-string conversion (for preparing date strings to be sent to the server) you might want to set the timeZone of the formatter to TimeZone(secondsFromGMT: 0).
Regarding the date formatter used to prepare the string representation of the date in your UI:
I would not advise ever using a fixed dd-MM-yyyy format in your UI. That might be natural for European users, but it may be unnatural to most US users, who generally expect to see month before the day.
I would suggest not using dateFormat for this second date formatter, but rather using a dateStyle (e.g. of .medium or .long). It results in a nice, localized, and natural reading date string.
If you insist in using dd and MM and yyyy in your UI, I’d localize it with setLocalizedDateFormatFromTemplate so that the day and the month appear in the logical order that this particular user would expect (month-followed-by-day for US users, day-followed-by-month for most other locales).
Thus:
let serverDateFormatter = DateFormatter()
serverDateFormatter.locale = Locale(identifier: "en_US_POSIX")
serverDateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
serverDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSX"
let uiFormatter = DateFormatter()
uiFormatter.dateStyle = .medium // or uiFormatter.setLocalizedDateFormatFromTemplate("ddMMyyyy")
if let date = serverDateFormatter.date(from: creationDateString) {
let string = uiFormatter.string(from: date)
// use that `string` in your UI
}

ISO8601DateFormatter accepts wrong input

I want to parse a date of the format yyyyMM to a date (e.g. 202007 should convert to July 2020). This works fine using DateFormatter - also when sending wrongly formatted input to it:
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMM"
let result = formatter.date(from: "***")// = nil
But after reading Apple's documentation for DateFormatter, I tried changing to ISO8601DateFormatter instead due to this phrase:
When working with date representations in ISO 8601 format, use
ISO8601DateFormatter instead.
However, I cannot make it work properly when sending invalid input to it:
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withMonth, .withYear]
let result = formatter.date(from: "***")//= 2000-01-01 00:00:00
Why does formatter.date return a date an not nil when sending a wrongly formatted string to it? Are there any way to make to above code return nil instead?

Getting string between exact letters by regex in Swift

I have a String in this format: "2019-03-11T17:04:00+0100". I need to convert that string to the one that will be in this format: "03.11 17:04". I already tried some suggestions for instance this one.
As per my comment, this is a task for DateFormatter rather than RegeX. I threw this together in a playground quickly to demonstrate what I mean.
let inFormatter = DateFormatter()
inFormatter.locale = Locale(identifier: "en_US_POSIX")
inFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let input = "2019-03-11T17:04:00+0100"
let dateFromInput = inFormatter.date(from: input)! // This should be unwrapped properly in your code.
let outFormatter = DateFormatter()
outFormatter.locale = Locale(identifier: "en_US_POSIX")
outFormatter.dateFormat = "MM. dd HH:mm"
let output = outFormatter.string(from: dateFromInput)
print(output) // Prints 03. 11 16:04.
The premise is that you provide a format for which to parse the input string against, this is transcoded to a Date object which you can then transcode to your desired output format with a second DateFormatter.
EDIT:
As pointed out by #user28434, the input you are passing in looks like CET (Central European Time); When I configure the output DateFormatter, I do not specify a time zone so it defaults to my local time zone, GMT (Greenwich Mean Time). This would obviously cause the output to be different based on the location of the user in the world, which should be expected/desired. But it's worth highlighting. You can use outFormatter.timeZone = TimeZone(identifier: "CET") to force a CET output.
You can use DateFormatter instead of regex,
first, convert the given string to a date with the string format,
then convert the resulted date to a string with the desired format.
func convertISO8601DateStringToDate(dateStr: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return dateFormatter.date(from: dateStr)
}
func convertDateToReadableOutput(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM.dd HH:mm"
return dateFormatter.string(from: date)
}
you can use these two methods as below:
if let date = stringToDateConverter(dateStr: "2019-03-11T17:04:00+0100") {
print(dateToStringConverter(date: date))
}

Got nil after string to date convertation

I need to convert String simular to "2016-07-10T21:32:20G" to Date.
But for some reason I've got only nil again and again.
I've read an article about date formater. And unfortunately haven't fount an answer there. I found something in the documentation. And documentation tels to read about Unicode Date Format Patterns. And it looks similar to mine.
Probably I missed something =(
My Code example. Unfortunately always get nil.
let lastUpdatedDateString = "2016-07-10 21:32:20"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-mm-dd hh:mm:ss"
let lastUpdated = dateFormatter.date(from:lastUpdatedDateString)
But this code works fine:
let lastUpdatedDateString = "2016-07-10"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-mm-dd"
let lastUpdated = dateFormatter.date(from:lastUpdatedDateString)
I'm testing it in the playground.
In fact I must convert this String("2016-07-10T21:32:20G") to Date.
PS
Anyway, thanks for attention =)
Think it's just the capitalisation in your formatter. Try this ...
let lastUpdatedDateString = "2016-07-10 21:32:20"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let lastUpdated = dateFormatter.date(from:lastUpdatedDateString)
This is what I get with the playground
let lastUpdatedDateString = "2016-07-10T21:32:20"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-ddEEEEEHH:mm:ss"
let lastUpdated = dateFormatter.date(from:lastUpdatedDateString)
print(lastUpdated)
and it prints: Optional(2016-07-10 19:32:20 +0000) (I'm GMT+2)
I don't know what the G stands for so I'm not sure how I can get the last character for the pattern. EEEEE stands for day of the week (1 character)
EDIT: It seems G stand for gregorian calendar
so you can parse the last letter out of the string and add this if it's gregorian calendar (but I suppose this is the default value):
dateFormatter.calendar = Calendar(identifier: .gregorian)
If the letter's a J it's Julian calendar then and you can see this answer to see how to convert gregorian to julian: https://stackoverflow.com/a/12137019/2106940