Convert date string to date object - ios

Actual string - January 31, 2020 at 11:59:59 p.m. (ET).
format - "MMMM d, yyyy 'at' hh:mm:ss aa"
Not able to convert this string to Date object. Tried various date format and but unable to change string to Date object.
Please help

You could delete the non-standard character . between p and m and escape the parentheses around the time zone
let dateString = "January 31, 2020 at 11:59:59 p.m. (ET)"
let trimmedDateString = dateString.replacingOccurrences(of: ".m.", with: "m")
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMMM d, yyyy 'at' hh:mm:ss a (v)"
let date = formatter.date(from: trimmedDateString)

let raw = "January 31, 2020 at 11:59:59 p.m. (ET)"
func getDateFrom(_ raw: String) -> Date? {
if let i = (raw.range(of: "(")?.lowerBound) { // get index of open parenthesis
var zone: TimeZone?
let dateString = String(raw.prefix(upTo: i)) // January 31, 2020 at 11:59:59 p.m.
let zoneString = String(raw.suffix(from: i)) // (ET)
switch zoneString { // determine time zone
case "(ET)", "(EST)", "(EDT)": // study your data source and learn how they may express time zones
zone = TimeZone(abbreviation: "EDT")
default:
return nil // failure
}
let formatter = DateFormatter()
formatter.dateFormat = "MMMM d, yyyy 'at' hh:mm:ss a"
formatter.timeZone = zone
formatter.amSymbol = "a.m."
formatter.pmSymbol = "p.m."
if let date = formatter.date(from: dateString) {
return date
} else {
return nil
}
} else {
return nil
}
}
if let date = getDateFrom(raw) {
print(date) // 2020-02-01 04:59:59 +0000
}
This should be a good starting point. There are a number of ways to do this, such as whether you want it to fail if a time zone can't be determined, how to extract the time zone (using abbreviations, identifiers, seconds from GMT), etc. I like vadian's answer if I was completely comfortable with the time zone. But time zones are so inconsistent and surprisingly non-standardized when expressed as strings, that I'd rather dedicate a mechanism to getting it and making sure it's valid.
That said, this is a relatively crude example of where I'd begin—a finished version would be more nuanced. In this example, if the time zone fails, the function fails.

Related

Get only time from Date

How to get only time from the date with am/pm. Below is my code what I have tried so far:
func changeFormat(str:String) -> String {
let dateFormatter = DateFormatter()
let newDateFormatter = DateFormatter()
// step 1
dateFormatter.dateFormat = "MM/dd/yyyy HH:mm aa" // input format
newDateFormatter.dateFormat = "HH:mm aa" // output format
let date = dateFormatter.date(from: str)!
// step 2
let string = newDateFormatter.string(from: date)
return string
}
Usage:
let strTimeFromDate = changeFormat(str: self.response?.Data[indexPath.row].ADateTime ?? "")
input date:
06/22/2021 2:00 PM
output:
12:45 PM
Getting wrong time return after formatting. Please guide what's wrong with above code
As mentioned by #JoakimDanielson in the comment, you are mixing HH (24 hours format) with hh (12 hours format). Using hh for the dateFormatter (given your input has 02:00 PM) should fix your issue (as long as this input value was captured in the same time zone in which you intend to convert).
In case you are still seeing that the output is not expected, then it is the issue with missing timeZone info in your input. You are trying to convert String > Date > String without specifying an input timeZone & output timeZone. You can try printing these values in your implementation.
print("dateFormatter.timeZone.secondsFromGMT() : \(dateFormatter.timeZone.secondsFromGMT())")
let date = dateFormatter.date(from: str)!
print("parsed date : \(date)")
print("\n ------------------------ \n")
// step 2
print("newDateFormatter.timeZone.secondsFromGMT() : \(newDateFormatter.timeZone.secondsFromGMT())")
let string = newDateFormatter.string(from: date)
print("parsed string : \(string)")
For me it prints following. 19800 / (60 * 60) = 5.5 (+05:30)
dateFormatter.timeZone.secondsFromGMT() : 19800
parsed date : 2021-06-22 06:30:00 +0000
------------------------
newDateFormatter.timeZone.secondsFromGMT() : 19800
parsed string : 12:00 PM
The issue is - for these two conversions to happen correctly - you must specify an appropriate time zone.
String to Date
Date to String
Here's what that implementation could look like. Assuming input is "06/22/2021 2:00 PM +0530"
func changeFormat(str: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(secondsFromGMT: Int(5.5*60*60)) // +05:30
let newDateFormatter = DateFormatter()
newDateFormatter.timeZone = TimeZone(secondsFromGMT: Int(5.5*60*60)) // +05:30
dateFormatter.dateFormat = "MM/dd/yyyy hh:mm aa Z"
newDateFormatter.dateFormat = "hh:mm aa"
let date = dateFormatter.date(from: str)!
let string = newDateFormatter.string(from: date)
return string
}
When you are consuming this date string from your backend server, you should consider using appropriate time zone for it. An example, assuming input is "06/22/2021 2:00 PM +0000" -
func changeFormat(str: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) // Server timeZone (UTC)
let newDateFormatter = DateFormatter()
newDateFormatter.timeZone = TimeZone.current // User's timeZone
dateFormatter.dateFormat = "MM/dd/yyyy hh:mm aa Z"
newDateFormatter.dateFormat = "hh:mm aa"
let date = dateFormatter.date(from: str)!
let string = newDateFormatter.string(from: date)
return string
}
"HH" is for 24h format and "aa" is for 12h format so you can't mix them, so you should use "hh:mm a" instead for both DateTimeFormatter's

Parse ISO8601 in swift

I am parsing 2020-06-09T18:11:26.904Z and it returns this 09 Jun, 11:11 pm it should be 06:11 pm
I have tried this solution Parsing a ISO8601 String to Date in Swift but it also works like the same.
Here's my code.
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
guard let date = df.date(from: dateTime) else {
return nil
}
df.dateFormat = "dd MMM, hh:mm a"
df.amSymbol = "am"
df.pmSymbol = "pm"
let stringFromDate = df.string(from: date)
return stringFromDate
I know I could ask it on the mentioned link but due to the new user I couldn't add comment on that.
You didn't set the timezone of the output date, so it's defaulting to your systems' timezone rather than UTC (which is what you were expecting).

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.

iOS Swift: how to convert specific date string format to Date object, when string contains time zone abbreviation?

I am getting a date string from server in the format of "March 08, 2018 16:00:00 PST" where there is a lot of whitespace between Month & Date.
My intention is to basically remove those extra whitespaces. My idea is that- I will convert the string into Date object, and then convert back to string.
How can I convert this into Date object using Date Formatter, taking timezone into consideration.
I am concerned about the "PST" here. While converting the Date to String, I will need in the format - "March 08, 2018 16:00:00 PST" i.e. PST (or whatever time zone comes in) should stay intact in the final string.
extension String {
func getDate(fromFormat format: String = "MMMM dd, yyyy HH:mm:ss zzz") -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
return dateFormatter.date(from: self)
}
}
let myDateString = "March 08, 2018 16:00:00 PST"
myDateString.getDate()
You can call with other time formats too.
Try this
let isoDate = "March 08, 2018 16:00:00 PST"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd, yyyy HH:mm:ss z"
let date = dateFormatter.date(from: isoDate)!
Try this
class func stringToDate (dateString:String, dateFormat:String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
let dateFromString = dateFormatter.date(from: dateString)
return dateFromString
}

Why can I not format my NSDate with the specified time zone with NSDateFormatter?

I want to get a date that is represented in a String with a time zone GMT+1 and display it on screen with the local time zone GMT+10.
I have 2 methods, one is for create a date from a String (with GMT+1 timeZone), the other one is to format the date into a String (with localTimeZone GMT+10):
func dateFromString(dateString: String) -> NSDate? {
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT+1")
dateFormatter.dateFormat = "M/d/yyyy hh:mma"
return dateFormatter.dateFromString(dateString)
}
func stringFromDate(date: NSDate) -> String {
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone()
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
return dateFormatter.stringFromDate(date)
}
In the playground, when I do this:
let date = dateFromString("4/8/2015 1:29am")!
println(date)
println(stringFromDate(date))
I get the following output on the right side:
"Apr 8, 2015, 1:29 AM"
"2015-04-07 15:29:00 +0000"
"1:29 AM"
I don't understand why I don't get what I am expecting and looking for:
"Apr 8, 2015, 1:29 AM"
"2015-04-08 10:29:00 +0000"
"10:29 AM"
What's wrong?
Input formatters need the time zone in their string. Like so:
func dateFromString(dateString: String) -> NSDate? {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "M/d/yyyy hh:mma z"
return dateFormatter.dateFromString(dateString)
}
let date = dateFromString("4/8/2015 1:29am GMT+01")!
Also note that NSTimeZone names have a two digit offset. Compare in the playground
var oops = NSTimeZone(abbreviation: "GMT+1")
var righteous = NSTimeZone(abbreviation: "GMT+01")
The first is nil, the second is not.
I wouldn't trust the formatting of a time stamp that the debugger prints. In my experience dates are always in UTC regardless of the time zone you set according to the log. Try adding the date as a string to a label on the project and see if it's right.
The reasoning behind this as far as I know is that when you print to the log, all it's doing is calling -description. In the case of NSDate this will return in UTC by definition.

Resources