Extracting the offset value from a ISO-8601 string in Swift - swift

With NSDateFormatter, if I pass in an ISO-8601 date+time value with an offset, it will read the offset and adjust the output to a UTC value, like so:
// Setup
let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
// Test
let input = "2016-04-16 04:12:00-0800"
let output = formatter.dateFromString( input )
let outputStr = formatter.stringFromDate( output )
print( outputStr ) // "2016-04-15 08:12:00-0000"
So far, so good.
But I want to get at that offset value myself. My application is a client for a webservice which preserves date-time offset information, so I must do the same (as if I blindly return outputStr to the webservice, it will have lost the offset information so the database won't know what the offset was when the datetime was collected.
NSDateFormatter only seems to accept timezone information for outputting dates, there's no way for me to get the numeric offset out of an input string.

'self explanatory' example
import Foundation
let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
// Test
let input = "2016-04-16 04:12:00-0800"
let output = formatter.dateFromString( input )
// with curent time zone (system default
let outputStrL = formatter.stringFromDate( output! )
// with time zone Z+0000
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
let outputStr = formatter.stringFromDate( output! )
// with time zone Z-0800
formatter.timeZone = NSTimeZone(forSecondsFromGMT: -8 * 3600)
let outputStr1 = formatter.stringFromDate( output! )
print("My local time:", outputStrL)
print("GMT+0000 time:", outputStr)
print("GMT-0800 tume:", outputStr1)
/*
My local time: 2016-04-16 14:12:00+0200
GMT+0000 time: 2016-04-16 12:12:00+0000
GMT-0800 tume: 2016-04-16 04:12:00-0800
*/
if you replace the input string with any of output strings (outputStr, outputStrL, outputStr1), NSDate created from it represents the same value (time)

Related

Swift Convert UTC string to UTC Interval

I am getting UTC date in string format from server which is "Sep 8 2022 10:14AM"
Now I want to convert it into same time interval like this 1662632040.
Below Is my code I am not getting why I am getting different time interval
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd yyyy HH:mma"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
let dt = dateFormatter.date(from: "Sep 8 2022 10:14AM")!
let interval = dt.timeIntervalSince1970
interval is giving result 1662596040 but correct one is 1662632040.
I am not getting what is missing

Swift get the remaining seconds of time on target string date

The current date time today was May 9, 2020 10:03 PM, and I have a target string date with the value of 2020-05-09 22:07:30 with the format of yyyy-MM-dd HH:mm:ss.
How can I get the remaining date from that 2 date and print the value with the string of 04:30 as the range of those 2 dates are 4 minutes and 30 seconds
What I can only do is convert the milliseconds to time format like
func msToTime(ms: Int) {
let seconds = ms % 60
let minutes = ms / 60000
return String(format: "%0.2d:%0.2d",minutes,seconds)
}
Output 04:30
But I don't know how to get the range of milliseconds from today's date time to target's date time.
Or if there's any other easier way to do it?
You can use Calendar and DateComponents to easily calculate differences between dates in whatever units you desire. For example, this gets the difference in minutes and seconds:
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateformatter.date(from: "2020-05-09 22:07:30")!
let now = Date()
let components = Calendar.current.dateComponents([.minute, .second], from: now, to: date)
print("difference: \(components.minute!):\(components.second!)")
A straightforward way, with no calculation of any kind needed:
let d1 = Date()
let s = "2020-05-09 22:07:30"
let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd HH:mm:ss"
f.timeZone = TimeZone.current // or whatever
if let d2 = f.date(from: s) {
let f = DateComponentsFormatter()
f.unitsStyle = .positional
let result = f.string(from: d1, to: d2)
}
If you don't like the resulting string format of result, you can eliminate pieces of it. — However, note that this works only because no full days are involved. It isn't clear what the range of possible inputs might be or what the desired output would be if the second date were three months into the future, for example.

How to convert date/time from GMT to local timezone

I'm getting a date time string from a JSON response that looks like this:
2019-07-18 13:39:05
This time is GMT. How can I convert this to locale time zone, in my case Eastern with day light savings.
func convertDateFormat(date:String) -> String {
let dateFormatterGet = DateFormatter()
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateStyle = .long
dateFormatterPrint.timeStyle = .short
let date = dateFormatterGet.date(from: date)
return dateFormatterPrint.string(from: date!)
}
In the code above the result should be July 18, 2019 at 9:39 AM
Thanks
The input is a fixed-format date string in GMT, therefore dateFormatterGet must have
the locale set to "en_US_POSIX" (otherwise it can default to the user's locale settings, compare What is the best way to deal with the NSDateFormatter locale "feechur"?), and
the time zone set to GMT (otherwise it defaults to the user's time zone):
Example:
let dateFormatterGet = DateFormatter()
dateFormatterGet.locale = Locale(identifier: "en_US_POSIX")
dateFormatterGet.timeZone = TimeZone(secondsFromGMT: 0)
dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"
Everything else should be OK: you don't set a time zone for dateFormatterPrint so that the user's time zone is used.

How to get a date from European time-only with DateFormatter in Swift

I try to get a date but I only have a time available.
For now, I don't care about year, month or day. All that matters is the time.
My input is in the form "hh:mm:ss" (i.e. European time format).
The problem is that any time above 12:59:59 does not work !
How can I get dates from times above 12:59:59 ????
My code looks like follows:
import UIKit
let str = "17:02:09"
let mydateFormatter = DateFormatter()
mydateFormatter.dateFormat = "hh:mm:ss"
let timezone = TimeZone(abbreviation: "CEST") ?? TimeZone.current
mydateFormatter.timeZone = timezone
mydateFormatter.locale = Locale(identifier: "de_DE") as Locale?
let date1 = mydateFormatter.date(from: str)
print(date1?.description ?? "no date available")
Output: no date available
If I set the str's hour setting to anything below or equal to 12, then it works.
For example, if I set str = "12:02:09", I get the output: 1999-12-31 23:02:09 +0000
For example, if I set str = "10:02:09, I get the output: 2000-01-01 09:02:09 +0000
For example, if I set str = "13:02:09, I get the output: no date available
How can I get dates from times above 12:59:59 ????
And ideally, how can I get dates that have a matching time with the input-time ?

Swift: get the offset value from a date object

as simple as it sounds, but it is hard to find my exact question in google.
I'm trying to ignore the UTC printed out value. I receive multiple dates, this one here is just an example: (it could be +0900, -0200, etc...)
"2017-05-01T12:30:00-0700"
once I apply it to a value using these lines:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
if let result = formatter.date(from: time) {print result}
the value of the dateTimeResult prints:
2017-05-01 19:30:00 UTC
Using swift date objects, how do I slice out the part "-0700", multiply the -7 or +7 (this example is negative) by minutes by seconds. I'll save that total as int in DB (I need it for categorizing the different timezones later). Then applying that total to the incoming date input using this line:
let output = Calendar.current.date(byAdding: .second, value: totalSecs, to: result)
The goal is to end up with this date:
"2017-05-01 12:30:00"
I already have a solution using string manipulation, but I don't think that is the ideal solution. If it must be done by string, how do you do it?
If I understand you correctly you want only the date and time portion ignoring always the time zone information.
In this case strip the time zone from the date string with regular expression
let dateString = "2017-05-01T12:30:00-0700"
let dateStringIgnoringTimeZone = dateString.replacingOccurrences(of: "[+-]\\d{4}", with: "", options: .regularExpression)
print(dateStringIgnoringTimeZone) // "2017-05-01T12:30:00"
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = dateFormatter.date(from: dateStringIgnoringTimeZone)!
I think you should keep the date as it is and then just use DateFormatter to display the time at that timezone
let time = "2017-05-01T12:30:00-0700"
let dateFormatter = DateFormatter()
dateFormatter.calendar = Calendar(identifier: .iso8601)
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
if let result = dateFormatter.date(from: time) {
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dateFormatter.string(from: result)) // "2017-05-01 16:30:00 (corresponding time at my location GMT-3)
// to display it at -0700 just set the formatter timaZone
dateFormatter.timeZone = TimeZone(secondsFromGMT: -3600 * 7)
print(dateFormatter.string(from: result)) // "2017-05-01 12:30:00\n"
}
To get the timezone offset from the string:
let hours = Int(time.suffix(5).prefix(3)) ?? 0
let minutes = Int(time.suffix(2)) ?? 0
let offset = hours * 3600 + minutes * 60
print(offset) // -25200