如何分析目标C中的ISO-8601持续时间?(How to parse an ISO-8601 du

2019-06-17 10:33发布

我正在寻找一个简单的方法来分析包含了ISO-8601 持续时间的目的C.结果应该是使用像一个字符串NSTimeInterval

一个ISO-8601持续时间的一个示例: P1DT13H24M17S ,这意味着1天,13小时,24分17秒。

Answer 1:

如果你确切地知道哪些字段,你会得到,你可以使用一个调用sscanf()

const char *stringToParse = ...;
int days, hours, minutes, seconds;
NSTimeInterval interval;
if(sscanf(stringToParse, "P%dDT%dH%dM%sS", &days, &hours, &minutes, &seconds) == 4)
    interval = ((days * 24 + hours) * 60 + minutes) * 60 + seconds;
else
    ; // handle error, parsing failed

如果任何字段可以省略,你必须聪明一点在你的分析,如:

const char *stringToParse = ...;
int days = 0, hours = 0, minutes = 0, seconds = 0;

const char *ptr = stringToParse;
while(*ptr)
{
    if(*ptr == 'P' || *ptr == 'T')
    {
        ptr++;
        continue;
    }

    int value, charsRead;
    char type;
    if(sscanf(ptr, "%d%c%n", &value, &type, &charsRead) != 2)
        ;  // handle parse error
    if(type == 'D')
        days = value;
    else if(type == 'H')
        hours = value;
    else if(type == 'M')
        minutes = value;
    else if(type == 'S')
        seconds = value;
    else
        ;  // handle invalid type

    ptr += charsRead;
}

NSTimeInterval interval = ((days * 24 + hours) * 60 + minutes) * 60 + seconds;


Answer 2:

纯目标C版...

NSString *duration = @"P1DT10H15M49S";

int i = 0, days = 0, hours = 0, minutes = 0, seconds = 0;

while(i < duration.length)
{
    NSString *str = [duration substringWithRange:NSMakeRange(i, duration.length-i)];

    i++;

    if([str hasPrefix:@"P"] || [str hasPrefix:@"T"])
        continue;

    NSScanner *sc = [NSScanner scannerWithString:str];
    int value = 0;

    if ([sc scanInt:&value])
    {
        i += [sc scanLocation]-1;

        str = [duration substringWithRange:NSMakeRange(i, duration.length-i)];

        i++;

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

NSLog(@"%@", [NSString stringWithFormat:@"%d days, %d hours, %d mins, %d seconds", days, hours, minutes, seconds]);


Answer 3:

这个版本解析每个YouTube的持续时间没有错误。
重要说明:此版本使用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];
}


Answer 4:

Swift2实现: https://github.com/Igor-Palaguta/YoutubeEngine/blob/swift-2.3/YoutubeEngine/Classes/Parser/NSDateComponents+ISO8601.swift

例如: let components = NSDateComponents(ISO8601String: "P1Y2M3DT4H5M6S")

测试: https://github.com/Igor-Palaguta/YoutubeEngine/blob/swift-2.3/Example/Tests/ISO8601DurationTests.swift

它还能够正确处理“P1M”和“PT1M”案件

Swift3实现: https://github.com/Igor-Palaguta/YoutubeEngine/blob/master/Source/YoutubeEngine/Parser/NSDateComponents%2BISO8601.swift

例如: let components = dateComponents(ISO8601String: "P1Y2M3DT4H5M6S")

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

更新2017年1月20日:增加了对周的支持



Answer 5:

稍微修改用户的功能

谢尔盖点

+ (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];
}


Answer 6:

下面是迅速的例子:(仅几个小时,分钟和秒)

func parseDuration(duration: String) -> Int {

var days = 0
var hours = 0
var minutes = 0
var seconds = 0

var decisionMaker = 0
var factor = 1

let specifiers: [Character] = ["M", "H", "T", "P"]

let length = count(duration)

for i in 1...length {

    let index = advance(duration.startIndex, length - i)
    let char = duration[index]

    for specifier in specifiers {
        if char == specifier {
            decisionMaker++
            factor = 1
        }
    }

    if let value = String(char).toInt() {

        switch decisionMaker {
            case 0:
                seconds += value * factor
                factor *= 10
            case 1:
                minutes += value * factor
                factor *= 10
            case 2:
                hours += value * factor
                factor *= 10
            case 4:
                days += value * factor
                factor *= 10
            default:
                break
        }
    }

}

return seconds + (minutes * 60) + (hours * 3600) + (days * 3600 * 24)
}


Answer 7:

下面是SWIFT 3版headkaze例子:这种格式是最适合我的情况:

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)

}


Answer 8:

我抬头一看这个维基百科的文章对以ISO-8601如何实际工作的参考。 我没有可可专家,但我敢打赌,如果你可以分析串并提取该组件时,分,秒,日等,得到它在一个NSTimeInterval应该很容易。 棘手的部分是分析它。 我可能会做这样的事情:

首先,在分割字符串以两个独立的字符串:一个代表天,代表一个时代。 的NSString有一个实例方法componentsSeparatedByString:的NSString返回你通过你传递参数分离的原件的NSString的子串的一个NSArray它看起来是这样的:

NSString* iso8601 = /*However you're getting your string in*/
NSArray* iso8601Parts = [iso8601 componentsSeparatedByString:@"T"];

接着,搜索iso8601Parts的第一元件,用于每一个可能接受天持续时间的指标(Y,M,W,和d)。 当你发现一个,抓住所有的前述数字(可能还有小数点),将它们转换为浮动,并将它们存储的地方。 请记住,在仅仅存在一个时间元素,然后iso8601Parts [0]将是空字符串。

然后,在做为iso8601Parts可能时间指示器(H,M,S)的所述第二元件寻找时间部分同样的事情。 请注意,如果有仅一天组分(即,有在原来的字符串没有“T”字符),然后iso8601Parts只会是一个长度,并尝试访问所述第二元件将导致越界异常的。

一个NSTimeInterval仅仅是一个长期存储的秒数,所以转换你拉出秒的各个部分,加在一起,将它们存储在您的NSTimeInterval,和你设置。

对不起,我知道你问一个“容易”的方式做到这一点,但基于我的(当然光)周围和API的知识搜索,这是做到这一点的最简单的方法。



Answer 9:

快速和肮脏的实施

    - (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;
}


Answer 10:

有答案了,但我最终实现用另一个版本NSScanner 。 该版本忽略年份和月份,因为它们不能被转换成秒数。

static NSTimeInterval timeIntervalFromISO8601Duration(NSString *duration) {
    NSTimeInterval timeInterval = 0;
    NSScanner *scanner = [NSScanner scannerWithString:duration];

    NSCharacterSet *designators = [NSCharacterSet characterSetWithCharactersInString:@"PYMWDTHMS"];
    BOOL isScanningTime = NO;

    while (![scanner isAtEnd]) {
        double scannedNumber = 0;
        BOOL didScanNumber = [scanner scanDouble:&scannedNumber];

        NSString *scanned = nil;
        if ([scanner scanCharactersFromSet:designators intoString:&scanned]) {
            if (didScanNumber) {
                switch ([scanned characterAtIndex:0]) {
                    case 'D':
                        timeInterval += scannedNumber * 60 * 60 * 24;
                        break;
                    case 'H':
                        timeInterval += scannedNumber * 60 * 60;
                        break;
                    case 'M':
                        if (isScanningTime) {
                            timeInterval += scannedNumber * 60;
                        }
                        break;
                    case 'S':
                        timeInterval += scannedNumber;
                        break;
                    default:
                        break;
                }
            }

            if ([scanned containsString:@"T"]) {
                isScanningTime = YES;
            }
        }
    }

    return timeInterval;
}


Answer 11:

现在Swift ! (是的,它是一个有点长,但它处理所有案件,单数/复数)。

把手年,月,周,日,小时,分钟和秒!

func convertFromISO8601Duration(isoValue: AnyObject) -> String? {

    var displayedString: String?
    var hasHitTimeSection = false
    var isSingular = false

    if let isoString = isoValue as? String {

        displayedString = String()

        for val in isoString {


            if val == "P" {
                // Do nothing when parsing the 'P'
                continue

            }else if val == "T" {
                // Indicate that we are now dealing with the 'time section' of the ISO8601 duration, then carry on.
                hasHitTimeSection = true
                continue
            }

            var tempString = String()

            if val >= "0" && val <= "9" {

                // We need to know whether or not the value is singular ('1') or not ('11', '23').
                if let safeDisplayedString = displayedString as String!
                    where count(displayedString!) > 0 && val == "1" {

                    let lastIndex = count(safeDisplayedString) - 1

                    let lastChar = safeDisplayedString[advance(safeDisplayedString.startIndex, lastIndex)]

                        //test if the current last char in the displayed string is a space (" "). If it is then we will say it's singular until proven otherwise.
                    if lastChar == " " {
                        isSingular = true
                    } else {
                        isSingular = false
                    }
                }
                else if val == "1" {
                    // if we are just dealing with a '1' then we will say it's singular until proven otherwise.
                    isSingular = true
                }
                else {
                    // ...otherwise it's a plural duration.
                    isSingular = false
                }

                tempString += "\(val)"

                displayedString! += tempString

            } else {

                // handle the duration type text. Make sure to use Months & Minutes correctly.
                switch val {

                case "Y", "y":

                    if isSingular {
                        tempString += " Year "
                    } else {
                        tempString += " Years "
                    }

                    break

                case "M", "m":

                    if hasHitTimeSection {

                        if isSingular {
                            tempString += " Minute "
                        } else {
                            tempString += " Minutes "
                        }
                    }
                    else {

                        if isSingular {
                            tempString += " Month "
                        } else {
                            tempString += " Months "
                        }
                    }

                    break

                case "W", "w":

                    if isSingular {
                        tempString += " Week "
                    } else {
                        tempString += " Weeks "
                    }

                    break

                case "D", "d":

                    if isSingular {
                        tempString += " Day "
                    } else {
                        tempString += " Days "
                    }

                    break

                case "H", "h":

                    if isSingular {
                        tempString += " Hour "
                    } else {
                        tempString += " Hours "
                    }

                    break

                case "S", "s":

                    if isSingular {
                        tempString += " Second "
                    } else {
                        tempString += " Seconds "
                    }

                    break

                default:
                    break

                }

                // reset our singular flag, since we're starting a new duration.
                isSingular = false

                displayedString! += tempString

            }

        }

    }

    return displayedString
}


Answer 12:

雨燕4.2版本

工程与年,月,日,时,分,秒。 秒钟就可浮点数。

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

期限结构:

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
}

}



文章来源: How to parse an ISO-8601 duration in Objective C?