Convert String minutes seconds to Int - ios

I've a string with minutes and seconds in format "minutes:seconds". For example, "5:36". I want to convert it to Int value. For example "5:36" string should be 336 Int value. How this can be done?

let timeString = "5:36"
let timeStringArray = timeString.split(separator: ":")
let minutesInt = Int(timeStringArray[0]) ?? 0
let secondsInt = Int(timeStringArray[1]) ?? 0
let resultInt = minutesInt * 60 + secondsInt
print(resultInt)

Here's a simple extension you can use which will validate the format of your input string too:
import Foundation
extension String {
func toSeconds() -> Int? {
let elements = components(separatedBy: ":")
guard elements.count == 2 else {
print("Provided string doesn't have two sides separated by a ':'")
return nil
}
guard let minutes = Int(elements[0]),
let seconds = Int(elements[1]) else {
print("Either the minute value or the seconds value cannot be converted to an Int")
return nil
}
return (minutes*60) + seconds
}
}
Usage:
let testString1 = "5:36"
let testString2 = "35:36"
print(testString1.toSeconds()) // prints: "Optional(336)"
print(testString2.toSeconds()) // prints: "Optional(2136)"

I tried out your example on the playground here's the code:
import Foundation
let time1String = "0:00"
let time2String = "5:36"
let timeformatter = DateFormatter()
timeformatter.dateFormat = "m:ss"
let time1 = timeformatter.date(from: time1String)
let time2 = timeformatter.date(from: time2String)
if let time1 = time1 {
print(time2?.timeIntervalSince(time1)) // prints: Optional(336.0)
}

Related

Swift - Not enough bits to represent the passed value

Below code works fine on iOS devices and watchOS simulator.
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}
However when I run it on Apple Watch Series 3, I get the following error: double value cannot be converted to int because the result would be greater than int.max on line let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000).
So I changed
let startTimeInMillis = Int(startDateTime.timeIntervalSince1970 * 1000) to let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
and
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((startTimeInMillis-fiveMinutesInMillis)/1000)) to let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000)).
Now I am getting following error: Not enough bits to represent the passed value on line let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
How do I change the function to make it work on Apple Watch Series 3 or watchOS 7?
Updated function code:
static func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let startTimeInMillis = Int64(startDateTime.timeIntervalSince1970 * 1000)
let fiveMinutesInMillis = 300000
let eventStartDateTime = Date(timeIntervalSince1970: TimeInterval((Int(startTimeInMillis)-fiveMinutesInMillis)/1000))
return convertDateToString(eventStartDateTime)
}
return ""
}
Use the Calendar API to add/subtract time units (doesn't support milliseconds, but does support nanoseconds, which can be converted from): https://developer.apple.com/documentation/foundation/calendar.
func getEventDateTimeCal(startDateTime: Date?) -> String {
if let startDateTime = startDateTime,
let date = Calendar.current.date(byAdding: .minute, value: -5, to: startDateTime) {
return convertDateToString(date, startDateTime)
}
return ""
}
But also, if you don't need millisecond precision, subtract seconds from TimeInterval. Note that TimeInterval is typealias TimeInterval = Double and always represents seconds.
func getEventDateTime(startDateTime: Date?) -> String {
if let startDateTime = startDateTime {
let fiveMinutesInSeconds = 5.0 * 60
let eventStartDateTime = Date(timeIntervalSince1970: startDateTime.timeIntervalSince1970 - fiveMinutesInSeconds)
return convertDateToString(startDateTime, eventStartDateTime)
}
return ""
}

Swift, converting a number to string, number gets rounded down

I'm having a bit of issue with my code...right now, I am passing a string containing a bunch of numbers, to get converted to a number, comma separators added, then converted back to a string for output. When I add a decimal to my string and pass it in, a number like 996.3658 get truncated to 996.366...
"currentNumber" is my input value, "textOutputToScreen" is my output...
func formatNumber() {
let charset = CharacterSet(charactersIn: ".")
if let _ = currentNumber.rangeOfCharacter(from: charset) {
if let number = Float(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
else {
if let number = Int(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
}
Thank you in advance for your help!
The issue there is that you have to set your NumberFormatter minimumFractionDigits to 4. Btw there is no need to initialize a NSNumber object. You can use Formatters string(for: Any) method and pass your Float. Btw I would use a Double (64-bit) instead of a Float (32-bit) and there is no need to initialize a new string g from your formattedNumber object. It is already a String.
Another thing is that you don't need to know the location of the period you can simply use contains instead of rangeOfCharacter method. Your code should look something like this:
extension Formatter {
static let number: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
}
func formatNumber(from string: String) -> String? {
if string.contains(".") {
guard let value = Double(string) else { return nil }
Formatter.number.minimumFractionDigits = 4
return Formatter.number.string(for: value)
} else {
guard let value = Int(string) else { return nil }
Formatter.number.minimumFractionDigits = 0
return Formatter.number.string(for: value)
}
}
let label = UILabel()
let currentNumber = "996.3658"
label.text = formatNumber(from: currentNumber) // "996.3658\n"
If you would like to assign the result to your var instead of a label
if let formatted = formatNumber(from: currentNumber) {
textOutputToScreen = formatted
}

Convert currency formatter to double swift

For some reason, I can't convert the Price string to double.
When I do it always returns nil.
func calculateAirfare(checkedBags: Int, distance: Int, travelers: Int) {
let bagsPrices = Double(checkedBags * 25)
let mileCosts = Double(distance) * 0.10
let price = (bagsPrices + mileCosts) * Double(travelers)
/// Format price
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
let priceString = currencyFormatter.string(from: NSNumber(value: price))
print(priceString) -> "Optional("$750.00")"
if let double = Double(priceString) {
print(double) -> nil
}
}
You can use your same formatter to go back to a number like so:
let number = currencyFormatter.number(from: priceString)
and get the doubleValue like:
let numberDouble = number.doubleValue
The price is already double from the line
let price = (bagsPrices + mileCosts) * Double(travelers)
thus no need to convert it to double.
The code below will return a string with a $ symbol
currencyFormatter.string(from: NSNumber(value: price))
To get a double from that string then you need to remove the $ symbol
which you can do using removeFirst()
priceString?.removeFirst()
After that, the string can be converted to Double.
The complete code is:
func calculateAirfare(checkedBags: Int, distance: Int, travelers: Int) {
let bagsPrices = Double(checkedBags * 25)
let mileCosts = Double(distance) * 0.10
let price = (bagsPrices + mileCosts) * Double(travelers)
/// Format price
let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currency
var priceString = currencyFormatter.string(for: price)
priceString?.removeFirst()
print(priceString!)
if let double = Double(priceString!) {
print(double)
}
}

How to parse a ISO 8601 duration format in Swift?

I have a function below which I use to format a string. The string is something like this "PT1H3M20S" which means 1 hour 3 minutes and 20 seconds. In my function, I want to format the string to 1:03:20 and it works fine but sometimes, I get the string like this "PT1H20S" which means 1 hour and 20 seconds and my function format it like this 1:20 which makes people read it as 1 minute and 20 seconds. Any suggestions?
func formatDuration(videoDuration: String) -> String{
let formattedDuration = videoDuration.replacingOccurrences(of: "PT", with: "").replacingOccurrences(of: "H", with:":").replacingOccurrences(of: "M", with: ":").replacingOccurrences(of: "S", with: "")
let components = formattedDuration.components(separatedBy: ":")
var duration = ""
for component in components {
duration = duration.count > 0 ? duration + ":" : duration
if component.count < 2 {
duration += "0" + component
continue
}
duration += component
}
// instead of 01:10:10, display 1:10:10
if duration.first == "0"{
duration.remove(at: duration.startIndex)
}
return duration
}
Call it:
print(formatDuration(videoDuration: "PT1H15S")
You can also just search the indexes of your hours, minutes and seconds and use DateComponentsFormatter positional style to format your video duration:
Create a static positional date components formatter:
extension Formatter {
static let positional: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
return formatter
}()
}
And your format duration method:
func formatVideo(duration: String) -> String {
var duration = duration
if duration.hasPrefix("PT") { duration.removeFirst(2) }
let hour, minute, second: Double
if let index = duration.firstIndex(of: "H") {
hour = Double(duration[..<index]) ?? 0
duration.removeSubrange(...index)
} else { hour = 0 }
if let index = duration.firstIndex(of: "M") {
minute = Double(duration[..<index]) ?? 0
duration.removeSubrange(...index)
} else { minute = 0 }
if let index = duration.firstIndex(of: "S") {
second = Double(duration[..<index]) ?? 0
} else { second = 0 }
return Formatter.positional.string(from: hour * 3600 + minute * 60 + second) ?? "0:00"
}
let duration = "PT1H3M20S"
formatVideo(duration: duration) // "1:03:20"
Since you need to see what unit is after each number, you can't start by removing the units from the string.
Here is a solution that uses Scanner to parse the original string and finds the number of hours, minutes, and seconds to build the final result.
This also changes the return value to be optional to indicate that the passed in string isn't valid.
func formatDuration(videoDuration: String) -> String? {
let scanner = Scanner(string: videoDuration)
if scanner.scanString("PT", into: nil) {
var hours = 0
var mins = 0
var secs = 0
let units = CharacterSet(charactersIn: "HMS")
while !scanner.isAtEnd {
var num = 0
if scanner.scanInt(&num) {
var unit: NSString?
if scanner.scanCharacters(from: units, into: &unit) {
switch unit! {
case "H":
hours = num
case "M":
mins = num
case "S":
secs = num
default:
return nil // Invalid unit
}
} else {
return nil // No unit after the number
}
} else {
return nil // No integer
}
}
if hours > 0 {
return String(format: "%d:%02d:%02d", hours, mins, secs)
} else {
return String(format: "%02d:%02d", mins, secs)
}
} else {
return nil // No leading PT
}
}
print(formatDuration(videoDuration: "PT1H3M20S") ?? "bad")
print(formatDuration(videoDuration: "PT1H15S") ?? "bad")
print(formatDuration(videoDuration: "PT4M6") ?? "bad")
Output:
1:03:20
1:00:15
bad
In your case, your string carries no character for minutes, so you can make a check if the string does not contain minutes, then add "00:" between 1:20 and format appropriately.

Convert Date String to Int Swift

I am trying to convert the string:
let time = "7:30"
to integers:
let hour : Int = 7
let minutes : Int = 30
I am currently looping through the string:
for char in time.characters {
}
But I cannot figure out how to convert a char to an int. Any help would be greatly appreciated.
Answers by #alex_p and #mixel are correct, but it's also possible to do it with Swift split function:
let time = "7:30"
let components = time.characters.split { $0 == ":" } .map { (x) -> Int in return Int(String(x))! }
let hours = components[0]
let minutes = components[1]
Use String.componentsSeparatedByString to split time string to parts:
import Foundation
let time = "7:30"
let timeParts = time.componentsSeparatedByString(":")
if timeParts.count == 2 {
if let hour = Int(timeParts[0]),
let minute = Int(timeParts[1]) {
// use hour and minute
}
}
If you do not want to import Foundation you can split time string to parts with:
let timeParts = time.characters.split(":").map(String.init)
You can split string by : character and then convert results to Int:
let timeStringArray = time.componentsSeparatedByString(":")
if timeStringArray.count == 2 {
hour = timeStringArray[0].toInt
minutes = timeStringArray[1].toInt()
}

Resources