Swift - Convert string to date - ios

On iOS 11+ only, I have this bug : When user has disabled 24h mode in phone hour settings, my string can't be parse to date using this code.
On other iOS version, there is no problems...
Is something missing to force 24h mode for the formatter ?
Variables date and beginHour came from webservice.
// input date = 20171201
// input beginHour = 2359
let dateTimeFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "fr_FR")
dateFormatter.timeZone = TimeZone.autoupdatingCurrent
dateTimeFormatter.dateFormat = "yyyyMMdd HHmm"
if let date = dateTimeFormatter.date(from: date+" "+beginHour) {
// ...
}

I think in your code, you are not using the right dateFormatter,
kindly check between dateTimeFormatter and dateFormatter,
let dateTimeFormatter = DateFormatter()
dateTimeFormatter.locale = Locale(identifier: "fr_FR")
dateTimeFormatter.timeZone = TimeZone.autoupdatingCurrent
dateTimeFormatter.dateFormat = "yyyyMMdd HHmm"
if let date = dateTimeFormatter.date(from: date+" "+beginHour) {
// ...
}

Related

Timezone in swift [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)
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"

DateIntervalFormatter's dateTemplate not consistent with DateFormatter's dateFormat in Swift

I am trying to display a date interval formatted using a custom format. The format which I am using works well for a date but do not so good when used with a date interval.
I pus as comments what is displayed and what I expect. Is there truly a problem or my expectations are wrong, in case they are wrong, why so? also, how can I achieve the expected part?
import UIKit
let now = Date()
let tomorrow = now.addingTimeInterval(24.0 * 3600.0)
let dateInterval = DateInterval(start: now, end: tomorrow)
// Initialize Date Interval Formatter
let dateIntervalFormatter = DateIntervalFormatter()
dateIntervalFormatter.dateTemplate = "dd-MM-yyyy"
dateIntervalFormatter.locale = Locale(identifier: "de")
dateIntervalFormatter.string(from: dateInterval)
// displays "12.–13.01.2022"
// expected "12-–13-01-2022"
dateIntervalFormatter.locale = Locale(identifier: "en")
dateIntervalFormatter.string(from: dateInterval)
// displays "1/12/2022 – 1/13/2022"
// expected "12-01-2022 – 13-01-2022"
dateIntervalFormatter.locale = Locale(identifier: "ro")
dateIntervalFormatter.string(from: dateInterval)
// displays "12.01.2022 – 13.01.2022"
// expected "12-01-2022 – 13-01-2022"
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
formatter.locale = Locale(identifier: "de")
let formatedTime = formatter.string(from: Date())
// displays "12-01-2022"
// expected "12-01-2022"
A locale/template based format means "I yield the details to you", so the DateIntervalFormatter does what it pleases depending on the locale. The DateFormatter example, on the other hand, is not locale/template based, so you get what you specified in the format (not template) you supplied.

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
}

dateFormat always returns nil

I'm getting a dateFormat from the server like this:
2016-09-04T17:44:44+02:00
So I wrote a dateformatter:
private static let jsonDateFormatter: NSDateFormatter = {
let fmt = NSDateFormatter()
fmt.dateFormat = "yyyy-MM-dd'T'HH:mm:ss+SSS"
return fmt
}()
there is still a problem because when I do jsonDateFormatter.dateFromString it returns nil so the dateFormat must be wrong.
I don't see the problem.
As per the Documentation given on Apple Docs here
The correct formatter for this type of Dates will be
yyyy-MM-dd'T'HH:mm:ssZZZZZ
As it is used in the give example
let RFC3339DateFormatter = NSDateFormatter()
RFC3339DateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
RFC3339DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
//RFC3339DateFormatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
/* 39 minutes and 57 seconds after the 16th hour of December 19th, 1996 with an offset of -08:00 from UTC (Pacific Standard Time) */
let string = "1996-12-19T16:39:57-08:00"
let date = RFC3339DateFormatter.dateFromString(string)
Wrong format string, plus you should also read Apple's Q&A on parsing internet date:
private static let jsonDateFormatter: NSDateFormatter = {
let fmt = NSDateFormatter()
fmt.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
fmt.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return fmt
}()

Swift dateFormat for variable dateType strings

I am trying to parse some json which is the result of an oData connection and I am getting the following response from the server:
"Task_ID":24,"Equipment_ID":3,"Owner_ID":2,"Priority":5,"Date_Due":
"2015-04-08T19:37:56.913","Time_Complete":"2015-04-09T19:37:56","Task_Description"
I am actually interested at the two different date fields I am receiving:
"Date_Due":"2015-04-08T19:37:56.913"
and
"Time_Complete":"2015-04-09T19:37:56"
As we can see, one has the millisecond timestamp and the other one does not.
By looking at the DB, this happens because the millisecond is actually .000 in the database (MS SQL Server) and for whatever reason, the result I receive in my json has this part truncated.
I am not interested in the milliseconds but I would like to have a dateFormat function that can handle both scenarios.
Now I have an obvious answer which is (pre)parse every date field, remove the milliseconds if they are there and then use the following code to format them:
let SQLDateFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
return formatter
}()
However I would like to know if we can build a formatter that can solve this issue without pre-parsing, something that would be able to take both:
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
and
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
and automatically do the formatting properly.
I have been checking for this issue but could not find anything out there, thanks in advance...
extension Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
static let iso8601withFractionalSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter
}()
}
extension String {
var date: Date? {
return Formatter.iso8601withFractionalSeconds.date(from: self) ??
Formatter.iso8601.date(from: self)
}
}
"2015-04-08T19:37:56.913".date // "Apr 8, 2015, 4:37 PM"
"2015-04-09T19:37:56".date // "Apr 9, 2015, 4:37 PM"
No, NSDateFormatter will return nil if the string is not in the exact dateFormat as specified.
What you can do instead of preprocessing the string, is just check if the string you got is with miliseconds or without.
The best way to do this is using Regex. The idea is to create the SQLDateFormatter like you normal, and then check if the string has miliseconds or not. If there are miliseconds included, just change the dateFormat - way better than parsing the string.
if let match = tes.rangeOfString("(\\d{4}-\\d\\d-\\d\\d[T](\\d\\d:){2}\\d\\d.\\d{3})", options: NSStringCompareOptions.RegularExpressionSearch)
{
SQLDateFormatter.format = "yyyy-MM-dd'T'HH:mm:ss.SSS"
}

Resources