Swift - date formatter returns unwanted time - swift

If I convert "2019-01-01T00:00:00+0000" to a date, I would like the date to be the date in the string - January 1, 2019. And if I calculate the number of seconds in the time portion, I would like the result to be zero.
The problem is, when I convert my string into a date, it is stored in the UTC timezone. My locale is set to "en_US_POSIX", and my time zone is set to current. My date formatter uses "yyyy-MM-dd'T'HH:mm:ssZ". I know that the final Z means UTC, but I can't seem to figure out the correct field symbol to get the results I want.
func convertToDate(_ dateString: String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = .current
let date = dateFormatter.date(from: dateString)
else {
print("DATE ERROR:", dateString)
return Date()
}
return date
}

If you know for sure that the date strings you wish to parse will always end with a timezone in the form +XXXX then you can trim off that timezone from the string and then parse the remaining string as local time.
func convertToDate(_ dateString: String) -> Date? {
let minusTZ = String(dateString.dropLast(5)) // Assume the string ends with a +9999 timezone
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let date = dateFormatter.date(from: minusTZ)
return date
}
This will give you a local date with the same date and time in the original string regardless of the timezone of the original string.

Related

How to format date from 12h to 24h in SwiftUI?

I am trying to format a 12h date to 24h date, but is not working, maybe I am doing something wrong. I'll share the code below so maybe you can help me.
"syncViewModel._order.deliveryDate" is the date that I am getting from the backend and is a string.
Text(dateFormatTime(date: syncViewModel._order.deliveryDate ?? ""), style: .time)
func dateFormatTime(date : String) -> Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
dateFormatter.timeZone = .current
dateFormatter.locale = NSLocale(localeIdentifier: "en_GB") as Locale
return dateFormatter.date(from: date) ?? Date.now
}
This is the format of my date : 2022-01-25T17:00:00
You use a date formatter to convert a date string to a Date. You can then use an additional date formatter, to directly return the time in the format that you want, as a string:
func timeIn24HourFormat(from date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
return dateFormatter.string(from: date)
}
That way you can use it in SwiftUI directly, without requesting the time style:
Text(timeIn24HourFormat(from: dateFormatTime(date: "2021-07-14T17:00:00")))
In addition to Jorge's answer, I edited the code to be able to accept a Bool to change between 12 and 24 hour format so the user can choose.
struct DateFormat {
// Change time from 12 hour to 24 hour format
static func timeHourFormat(from date: Date, twentyFourHourFormat: Bool) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = twentyFourHourFormat ? "HH:mm" : "hh:mm"
return dateFormatter.string(from: date)
}
}
Then to apply the change (in SwiftUI) you can save the state first with:
#AppStorage("twentyFourHourFormat") var twentyFourHourFormat = true
And call the function with
Text(DateFormat.timeHourFormat(from: myDateTime ?? Date.now, twentyFourHourFormat: twentyFourHourFormat))
Just choose a toggle button or some or conditional setter to change between true and false.
myDateTime is taken from a Core Data return, hence the optional binding within the function call, but you can omit this and the ?? just use Date.now.

Where the time coming from when converting date string without any time in to Date() in swift?

I'm using this extension to convert a string containing date to Date() object:
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
let date = dateFormatter.date(from: self)
return date
}
}
the result always containing a time in it. I'm curious where is the time coming from, why it is not all zero?
print("11/12/2021".toDate())
result is ->
2020-12-19 21:00:00 +0000
In the time that I run the code, it is showing 21:00:00, so why it is 21? I believe It is not related to my time because I run it at different times.
A Date object indicates an instant in time anywhere on the planet, independent of time zone.
A DateFormatter can convert a String to a Date (or a Date to a String, but ignore that for now). When it converts a String to a Date, it may make assumptions about the time of day if that is not included in the String. I believe it assumes that the time is midnight (00:00:00) in the date formatter's time zone. (And by the way, midnight is the starting point of a day, so midnight is zero hours/minutes/seconds into the day. Essentially midnight IS zeros for time.)
So when you call your String extension to convert "11/12/2021" to a Date, the extension creates a DateFormatter which defaults to the device time zone. It creates a Date assuming Midnight in the local time zone.
When you print that date, it gets displayed in GMT.
It looks like your format string has problems though. You're getting the wrong year and month. I think you must be using the wrong month or day string in your formatter. (I always have to look those up when I use them.)
Edit:
You likely want a format string of "MM-dd-yyyy"
(2-digit month, 2-digit day of month, and 4-digit year.)
Lower-case "m" or "mm" is minutes. Upper-case "Y" is for "week of year" based calendars, which you probably don't want.
Try this code:
func toDate() -> Date?{
let dateFormatter = DateFormatter()
let posixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.locale = posixLocale
let date = dateFormatter.date(from: self)
return date
}
}
And to use it:
let dateString = "12/11/2021"
let date = dateString.toDate()
print(date)
if let date = date {
let convertedDateString = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .medium)
print(convertedDateString)
} else {
print("Can't convert \(dateString) to a date")
}
That displays "Dec 11, 2021 at 12:00:00 AM" in my US locale (US Date formatting.) Note that since I use the DateFormatter class method localizedString(from:dateStyle:timeStyle:) I see midnight as the displayed time (The time you get from a DateFormatter when you don't specify a time, but displayed in the local time zone.)
The answer is:
when we are converting a string to a Date Object the important part is the time zone that we are converting it to.
for example, if you convert your string date to a UTC time zone when you want to bring it back you have to set the time zone of the date to UTC.
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(identifier: "UTC")
so this is the reason why when we are printing the Date() object it is deferred from our string date.
extension String {
func toDate() -> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
let date = dateFormatter.date(from: self)
return date
}
}
extension Date {
func toString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm-dd-yyyy"
return dateFormatter.string(from: self)
}
}
let stringDate = "01-12-2021"
let date = "01-12-2021".toDate()
let convertBack = date?.toString()
print("(\(stringDate)) -> (\(date!)) -> (\(convertBack!))")
and the result is:
(01-12-2021) -> (2021-01-11 21:01:00 +0000) -> (01-12-2021)
so at the end when we convert back the Date object it will be the same. because that 2 dateFormatter in the extensions are using the default time zone. and if you want to specify a specific time zone you have to declare it in converting from and to string together.

How to add specified duration to string date in Swift 5

So I have a function that converts a specified local time, to UTC (Time starts and ends as a string)
I need to add a duration (lets say 1.5 hours) to this time, which may end up going into the next day so I believe I need to use Calendar, and not timeInterval.
I'm a little clueless on how this is done, the documentation isn't the greatest on this and I'm not good with Swift.
Here is what I have so far.
import Foundation
func localToUTC(date:String, originTimeZone:String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yyyy HH:mm"
dateFormatter.timeZone = TimeZone(identifier: originTimeZone)
let dt = dateFormatter.date(from: date)
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "MM/dd/yyyy HH:mm"
return dateFormatter.string(from: dt!)
}
print(localToUTC(date: "05/01/2021 15:37", originTimeZone: "America/Boise"))
Just to be clear, I am trying to add another parameter to my function (or make a new function, doesn't matter) to add a duration to the UTC time that my current function outputs.
To put things into context, let's say a flight departs at 15:37 local time (Boise, for example, which is +6 for UTC conversion).
So the flight departs at 21:37 UTC on 5/1/2021. The flight duration is 4 hours.
I would like an output of 5/2/2021 01:37 UTC.
Just add the flight duration to the resulting date object. Note also that you shouldn't force unwrap the result as it might crash your app. Make sure to return nil in case you pass an invalid string. Something like:
func localToUTC(date: String, originTimeZone: String, duration: TimeInterval) -> String? {
let dateFormatter = DateFormatter()
dateFormatter.locale = .init(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM/dd/yyyy HH:mm"
guard let timezone = TimeZone(identifier: originTimeZone) else { return nil }
dateFormatter.timeZone = timezone
guard let dt = dateFormatter.date(from: date) else { return nil }
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
return dateFormatter.string(from: dt.addingTimeInterval(duration))
}
print(localToUTC(date: "05/01/2021 15:37", originTimeZone: "America/Boise", duration: 4 * 60 * 60) ?? "nil")
This will print
05/02/2021 01:37

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))
}

How to convert a String to NSdate?

I am trying to convert fajerTime to NSDate. When I compile the project the dateValue is nil. Any idea how to fix this issue?
if prayerCommingFromAdan.id == 0 && prayerCommingFromAdan.ringToneId != 0{
// NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(YourClassName.methodOfReceivedNotification(_:)), name:"NotificationIdentifier", object: nil)
let fajerTime = "\(prayer0.time[0...1]):\(prayer0.time[3...4])" as String
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(fajerTime) as NSDate!
print(dateValue)
var dateComparisionResult:NSComparisonResult = NSDate().compare(dateValue)
if dateComparisionResult == NSComparisonResult.OrderedDescending {
addNotificationAlarm(year, month: month, day: day, hour: prayer0.time[0...1], minutes: prayer0.time[3...4], soundId: prayerCommingFromAdan.ringToneId, notificationBody: "It is al fajr adan")
}
The problem seems be the format of fajerTime. It looks like fajerTime is a time string, e.g. 12:34, whereas the date formatter is configured to accept string containing a month, day and year, e.g. 24-07-2016.
You need to format fajerTime to include the year, month and day, as well as the time. Also configure the date formatter to accept the full date and time.
Assuming prayer0 is an array, you will also need to combine the elements into a string, using joinWithSeparator.
e.g.
let hours = prayer0.time[0...1].joinWithSeparator("")
let minutes = prayer0.time[3...4].joinWithSeparator("")
let fajerTime = "\(month)-\(day)-\(year) \(hours):\(minutes)"
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy hh:mm"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(fajerTime) as NSDate!
Please follow example (Swift 3):
let dateStr = "2016-01-15 20:10:01 +0000"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let myDate = dateFormatter.date(from: dateStr)
Keep in mind:
Date format e.g. "yyyy-MM-dd HH:mm:ss Z" must match the date string pattern
In your case, your date string contains only time, yet, your date
formatter contains only date
When using dateFormatter.date() there is no need to cast it to Date as it returns a Date:
Helpful website for date formats:
http://nsdateformatter.com/