How to parse an ISO-8601 duration in Objective C?

前端 未结 12 626
清歌不尽
清歌不尽 2020-12-01 06:52

I\'m looking for an easy way to parse a string that contains an ISO-8601 duration in Objective C. The result should be something usable like a NSTimeI

相关标签:
12条回答
  • 2020-12-01 07:20

    This version parse every youtube duration without errors.
    Important: This version use ARC.

    - (NSString*)parseISO8601Time:(NSString*)duration
    {
        NSInteger hours = 0;
        NSInteger minutes = 0;
        NSInteger seconds = 0;
    
        //Get Time part from ISO 8601 formatted duration http://en.wikipedia.org/wiki/ISO_8601#Durations
        duration = [duration substringFromIndex:[duration rangeOfString:@"T"].location];
    
        while ([duration length] > 1) { //only one letter remains after parsing
            duration = [duration substringFromIndex:1];
    
            NSScanner *scanner = [[NSScanner alloc] initWithString:duration];
    
            NSString *durationPart = [[NSString alloc] init];
            [scanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] intoString:&durationPart];
    
            NSRange rangeOfDurationPart = [duration rangeOfString:durationPart];
    
            duration = [duration substringFromIndex:rangeOfDurationPart.location + rangeOfDurationPart.length];
    
            if ([[duration substringToIndex:1] isEqualToString:@"H"]) {
                hours = [durationPart intValue];
            }
            if ([[duration substringToIndex:1] isEqualToString:@"M"]) {
                minutes = [durationPart intValue];
            }
            if ([[duration substringToIndex:1] isEqualToString:@"S"]) {
                seconds = [durationPart intValue];
            }
        }
    
        return [NSString stringWithFormat:@"%02d:%02d:%02d", hours, minutes, seconds];
    }
    
    0 讨论(0)
  • 2020-12-01 07:24

    Swift 4.2 version

    Works with years, months, days, hours, minutes, seconds. Seconds can be float number.

    extension String{
        public func parseISO8601Time() -> Duration {
    
            let nsISO8601 = NSString(string: self)
    
            var days = 0, hours = 0, minutes = 0, seconds: Float = 0, weeks = 0, months = 0, years = 0
            var i = 0
    
            var beforeT:Bool = true
    
            while i < nsISO8601.length  {
    
                var str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i))
    
                i += 1
    
                if str.hasPrefix("P") || str.hasPrefix("T") {
                    beforeT = !str.hasPrefix("T")
                    continue
                }
    
                let scanner = Scanner(string: str)
                var value: Float = 0
    
                if scanner.scanFloat(&value) {
    
                    i += scanner.scanLocation - 1
    
                    str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i))
    
                    i += 1
    
                    if str.hasPrefix("Y") {
                        years = Int(value)
                    } else if str.hasPrefix("M") {
                        if beforeT{
                            months = Int(value)
                        }else{
                            minutes = Int(value)
                        }
                    } else if str.hasPrefix("W") {
                        weeks = Int(value)
                    } else if str.hasPrefix("D") {
                        days = Int(value)
                    } else if str.hasPrefix("H") {
                        hours = Int(value)
                    } else if str.hasPrefix("S") {
                        seconds = value
                    }
                }
            }
    
            return Duration(years: years, months: months, weeks: weeks, days: days, hours: hours, minutes: minutes, seconds: seconds)
    }      
    

    Duration struct:

    public struct Duration {
    
    let daysInMonth: Int = 30
    let daysInYear: Int = 365
    
    var years: Int
    var months: Int
    var weeks: Int
    var days: Int
    var hours: Int
    var minutes: Int
    var seconds: Float
    
    public func getMilliseconds() -> Int{
        return Int(round(seconds*1000)) + minutes*60*1000 + hours*60*60*1000 + days*24*60*60*1000 + weeks*7*24*60*60*1000 + months*daysInMonth*24*60*60*1000 + years*daysInYear*24*60*60*1000
    }
    
    public func getFormattedString() -> String{
    
        var formattedString = ""
    
        if years != 0{
            formattedString.append("\(years)")
            formattedString.append(" ")
            formattedString.append(years == 1 ? "year".localized() : "years".localized())
            formattedString.append(" ")
        }
    
        if months != 0{
            formattedString.append("\(months)")
            formattedString.append(" ")
            formattedString.append(months == 1 ? "month".localized() : "months".localized())
            formattedString.append(" ")
        }
    
        if weeks != 0{
            formattedString.append("\(weeks)")
            formattedString.append(" ")
            formattedString.append(weeks == 1 ? "week".localized() : "weeks".localized())
            formattedString.append(" ")
        }
    
        if days != 0{
            formattedString.append("\(days)")
            formattedString.append(" ")
            formattedString.append(days == 1 ? "day".localized() : "days".localized())
            formattedString.append(" ")
        }
    
        if seconds != 0{
            formattedString.append(String(format: "%02d:%02d:%.02f", hours, minutes, seconds))
        }else{
            formattedString.append(String(format: "%02d:%02d", hours, minutes))
        }
    
        return formattedString
    }
    

    }

    0 讨论(0)
  • 2020-12-01 07:27

    Here is swift 3 version of headkaze example: This format was most suitable in my case:

    private func parseISO8601Time(iso8601: String) -> String {
    
        let nsISO8601 = NSString(string: iso8601)
    
        var days = 0, hours = 0, minutes = 0, seconds = 0
        var i = 0
    
        while i < nsISO8601.length  {
    
            var str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i))
    
            i += 1
    
            if str.hasPrefix("P") || str.hasPrefix("T") { continue }
    
            let scanner = Scanner(string: str)
            var value = 0
    
            if scanner.scanInt(&value) {
    
                i += scanner.scanLocation - 1
    
                str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i))
    
                i += 1
    
                if str.hasPrefix("D") {
                    days = value
                } else if str.hasPrefix("H") {
                    hours = value
                } else if str.hasPrefix("M") {
                    minutes = value
                } else if str.hasPrefix("S") {
                    seconds = value
                }
            }
        }
    
        if days > 0 {
            hours += 24 * days
        }
    
        if hours > 0 {
            return String(format: "%d:%02d:%02d", hours, minutes, seconds)
        }
    
        return String(format: "%d:%02d", minutes, seconds)
    
    }
    
    0 讨论(0)
  • 2020-12-01 07:28

    Quick and dirty implementation

        - (NSInteger)integerFromYoutubeDurationString:(NSString*)duration{
    
        if(duration == nil){
            return 0;
        }
    
        NSString *startConst = @"PT";
        NSString *hoursConst = @"H";
        NSString *minutesConst = @"M";
        NSString *secondsConst = @"S";
        NSString *hours = nil;
        NSString *minutes = nil;
        NSString *seconds = nil;
        NSInteger totalSeconds = 0;
    
        NSString *clean = [duration componentsSeparatedByString:startConst][1];
    
        if([clean containsString:hoursConst]){
            hours = [clean componentsSeparatedByString:hoursConst][0];
            clean = [clean componentsSeparatedByString:hoursConst][1];
            totalSeconds = [hours integerValue]*3600;
        }
        if([clean containsString:minutesConst]){
            minutes = [clean componentsSeparatedByString:minutesConst][0];
            clean = [clean componentsSeparatedByString:minutesConst][1];
            totalSeconds = totalSeconds + [minutes integerValue]*60;
        }
        if([clean containsString:secondsConst]){
            seconds = [clean componentsSeparatedByString:secondsConst][0];
            totalSeconds = totalSeconds + [seconds integerValue];
        }
    
        return totalSeconds;
    }
    
    0 讨论(0)
  • 2020-12-01 07:33

    Swift3,4,5 implementation: https://github.com/Igor-Palaguta/YoutubeEngine/blob/master/Source/YoutubeEngine/Parser/NSDateComponents%2BISO8601.swift

    Example: let components = try DateComponents(ISO8601String: "P1Y2M3DT4H5M6S")

    Tests: https://github.com/Igor-Palaguta/YoutubeEngine/blob/master/Tests/YoutubeEngineTests/ISO8601DurationTests.swift

    Update: fixed for DougSwith case "P3W3DT20H31M21"

    0 讨论(0)
  • 2020-12-01 07:38

    slightly modifying function of the user

    Sergei Pekar

    + (NSString*)parseISO8601Time:(NSString*)duration
    {
        NSInteger hours = 0;
        NSInteger minutes = 0;
        NSInteger seconds = 0;
    
        //Get Time part from ISO 8601 formatted duration http://en.wikipedia.org/wiki/ISO_8601#Durations
        if ([duration rangeOfString:@"T"].location == NSNotFound || [duration rangeOfString:@"P"].location == NSNotFound) {
            NSLog(@"Time is not a part from ISO 8601 formatted duration");
            return @"0:00 Error";
        }
    
        duration = [duration substringFromIndex:[duration rangeOfString:@"T"].location];
    
        while ([duration length] > 1) { //only one letter remains after parsing
            duration = [duration substringFromIndex:1];
    
            NSScanner *scanner = [[NSScanner alloc] initWithString:duration];
            NSString *durationPart = [[NSString alloc] init];
            [scanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] intoString:&durationPart];
    
            NSRange rangeOfDurationPart = [duration rangeOfString:durationPart];
    
            if ((rangeOfDurationPart.location + rangeOfDurationPart.length) > duration.length) {
                NSLog(@"Time is not a part from ISO 8601 formatted duration");
                return @"0:00 Error";
            }
    
            duration = [duration substringFromIndex:rangeOfDurationPart.location + rangeOfDurationPart.length];
    
            if ([[duration substringToIndex:1] isEqualToString:@"H"]) {
                hours = [durationPart intValue];
            }
            if ([[duration substringToIndex:1] isEqualToString:@"M"]) {
                minutes = [durationPart intValue];
            }
            if ([[duration substringToIndex:1] isEqualToString:@"S"]) {
                seconds = [durationPart intValue];
            }
        }
    
        if (hours != 0)
            return [NSString stringWithFormat:@"%ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds];
        else
            return [NSString stringWithFormat:@"%ld:%02ld", (long)minutes, (long)seconds];
    }
    
    0 讨论(0)
提交回复
热议问题