Swift DateFormatter - Parse from UTC String - swift

I'm using a String extension to parse utc string to Date. But the parsed date is wrong. Not sure what i'm missing.
extension String {
var parseUTCDateTime:Date? {
let parser = DateFormatter()
parser.dateFormat = "MM/dd/yyyy HH:mm:ss a"
if let result = parser.date(from: self) {
return result
}
return nil
}
When I do "3/7/2022 7:40:17 AM".parseUTCDateTime, the result returned 2022-03-06 19:10:17 UTC.
Ideally the result should be 2022-03-06 07:10:17 UTC as the string contains AM in it.

Format strings have type-safe interpolation now, so you don't need to worry about the hh vs HH anymore that tripped you up.
try Date(utc: "3/7/2022 7:40:17 AM")
extension Date {
init(utc string: ParseStrategy.ParseInput) throws {
try self.init(
string,
strategy: ParseStrategy(
format: """
\(month: .defaultDigits)\
/\(day: .defaultDigits)\
/\(year: .defaultDigits)
\(hour: .defaultDigits(clock: .twelveHour, hourCycle: .oneBased))\
:\(minute: .defaultDigits)\
:\(second: .defaultDigits)
\(dayPeriod: .standard(.narrow))
""",
timeZone: .init(abbreviation: "UTC")!,
isLenient: false
)
)
}
}

Related

Convert Int to date formatter followed with timezone

Trying to convert Int to formatted string date with supplied timezone value and formatter to expected result "12/01/2022 09:03:41 PM THA".
//Response Data.
{
"code": "THA",
"name": "Thailand Standard Time (THA) (UTC+07)",
"offset": 25200,
"utc_offset": "UTC+07",
"region": "Asia/Phnom_Penh"
}
I have picked Timezone from api response code, Here is what I have implemented.
var timezoneCode = response.timeZone.code ?? ""
if let updatedOn = self.dict?["updated_on"]?.intValue{ // i.e., 1669903421000
let dateValue = Date(milliseconds: Int64(updatedOn))
stringDate = dateToStringTimeZone(date: dateValue, dateFormat: "MM/dd/yyyy hh:mm:ss a zzz", timeZone: timezoneCode)
}
// convert Data To String with Timezone / DateFormate
func dateToStringTimeZone(date: Date, dateFormat: String, timeZone: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
dateFormatter.locale = Locale.init(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone.init(abbreviation: timeZone)
let dateStr = dateFormatter.string(from: date)
return dateStr
}
// Date Extension
extension Date {
var millisecondsSince1970: Int64 {
return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
}
init(milliseconds: Int64) {
self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
}
}
After Trying it the above code resultant String date
"12/01/2022 07:33:41 PM GMT+5:30"
Can any one guild how to achive the formatted date value as shown below?
"12/01/2022 09:03:41 PM THA"
Replace "zzz" with your wanted timezone code directly in the date format string
"MM/dd/yyyy hh:mm:ss a '\(timezoneCode)'"
You also need to use another init for TimeZone, using the offset property seems to work
let offset = response.timeZone.offset
dateFormatter.timeZone = TimeZone(secondsFromGMT: offset)

Converting string to date returns nil

I am trying to convert my string to a date using a static date formatter. When I make the call to stringToDate() using the variables below, a nil value is returned.
I've checked previous posts about this issue where people are saying it's because of the dateformatter locale or timeZone. However, that doesn't seem to be the issue in this case.
Does anyone know what the issue could be in this case? My code is below:
import Foundation
class DateHelper {
private static let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm:ss"
df.locale = Locale(identifier: "en_GB")
df.timeZone = TimeZone.current
return df
}()
static func stringToDate(str: String, with dateFormat: String) -> Date? {
dateFormatter.dateFormat = dateFormat
let date = dateFormatter.date(from: str)
return date
}
}
var myDate = Date()
var dateStr = "2019-02-19T17:10:08+0000"
print(DateHelper.stringToDate(str: dateStr, with: "MMM d yyyy")) // prints nil
Looks like your string is in ISO8601 format. Use the ISO8601DateFormatter to get date instance. You can use ISO8601DateFormatter.Options to parse varieties of ISO8601 formats. For your string,
For Swift 4.2.1
let formatter = ISO8601DateFormatter()
let date = formatter.date(from: dateStr)
print(date!)
Should output
"2019-02-19 17:10:08 +0000\n"
Your date format doesn't match your input date. Try this code:
print(DateHelper.stringToDate(str: dateStr, with: "yyyy-MM-dd'T'HH:mm:ssZZZZZ"))
Hope this helps.

Swift converting Eureka date into String for firebase storing

I am attempting to collect the date value from a Eureka DateTimeRow to then store it into Firebase but to store it I would need it to be in a string format. I have attempted this conversion but I receive the error 'Could not cast value of type 'Foundation.Date' (0x108af27e8) to 'Swift.String' (0x1086e99f8).'
I would like to know if there is something I am missing from my conversion method.
DateTimeRow:
<<< DateTimeRow("startDate"){
$0.title = "Start Date"
$0.value = NSDate() as Date
$0.cellUpdate { (cell, row) in
cell.datePicker.minimumDate = Date()
}
$0.onChange { row in
start = row.value!
}
}
Code getting the values of the Erueka form and converting:
let valuesDictionary = form.values()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let formattedDate = formatter.date(from: valuesDictionary["startDate"] as! String)
Thank you all feedback welcomed.
As you want to convert from a Date (Eureka) to a String (Firebase), you should use the string(from:) method of the DateFormatter, whereas you are attempting to use the date(from:) method.
// Date to String
func string(from date: Date) -> String
// String to Date
func date(from string: String) -> Date?

Date string conversion doesn't return decimal miliseconds Swift

I need to check if a date string is of this exact format 2017-01-01 00:00:00.000 with 3 decimal milliseconds, otherwise return.
I'm using yyyy-MM-dd hh:mm:ss.SSS but this doesn't return any decimal milliseconds. Why is that? When I convert Date() to string with DateFormatter, then it returns correct ss.SSS.
let str = "2017-01-01 00:00:00.000"
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
df.timeZone = NSTimeZone(abbreviation: "GMT") as TimeZone!
if let date = df.date(from: str) {
print("date: \(date)")
} else {
print("no")
}
output: date: 2017-01-01 00:00:00 +0000
More, there is no error if I add ss.S/ss.SS all return true. why?
The date formatter patterns says that 1...n digits for the milliseconds is valid. You can write as many capital S as you want - it makes no difference. You have to check for the second condition separately:
extension String {
var endsWithThreeDecimalDigits: Bool {
return self.range(of: "\\.\\d{3}$", options: .regularExpression) != nil
}
}
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
df.timeZone = TimeZone(abbreviation: "GMT")
if let date = df.date(from: str), str.endsWithThreeDecimalDigits {
print("date: \(date)")
} else {
print("no")
}

How to handle two possible date formats?

My app calls a web api that sometimes returns json dates in this format:
"2017-01-18T10:49:00Z"
and sometimes in this format:
"2017-02-14T19:53:38.1173228Z"
I can use the following dateformat to convert the 2nd one to a Date object:
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
But of course it doesn't work for the 1st one.
I've tried utilities like https://github.com/melvitax/DateHelper to see if it will work, but I haven't found a way to convert a json date (in any format) into a Date object.
Any recommendations?
Try both formats:
let parser1 = DateFormatter()
parser1.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
let parser2 = DateFormatter()
parser2.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
func parse(_ dateString: String) -> Date? {
let parsers = [parser1, parser2]
for parser in parsers {
if let result = parser.date(from: dateString) {
return result
}
}
return nil
}
print(parse("2017-01-18T10:49:00Z"))
print(parse("2017-02-14T19:53:38.1173228Z"))
Also note that the Z in the format is not a literal value.