How to parse a ISO 8601 duration format in Swift?

后端 未结 3 594
夕颜
夕颜 2021-01-24 15:57

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 fo

相关标签:
3条回答
  • 2021-01-24 16:31

    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.

    0 讨论(0)
  • 2021-01-24 16:48

    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

    0 讨论(0)
  • 2021-01-24 16:51

    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.index(of: "H") {
            hour = Double(duration[..<index]) ?? 0
            duration.removeSubrange(...index)
        } else { hour = 0 }
        if let index = duration.index(of: "M") {
            minute = Double(duration[..<index]) ?? 0
            duration.removeSubrange(...index)
        } else { minute = 0 }
        if let index = duration.index(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"
    
    0 讨论(0)
提交回复
热议问题