我正在寻找一个简单的方法来分析包含了ISO-8601 持续时间的目的C.结果应该是使用像一个字符串NSTimeInterval
。
一个ISO-8601持续时间的一个示例: P1DT13H24M17S
,这意味着1天,13小时,24分17秒。
我正在寻找一个简单的方法来分析包含了ISO-8601 持续时间的目的C.结果应该是使用像一个字符串NSTimeInterval
。
一个ISO-8601持续时间的一个示例: P1DT13H24M17S
,这意味着1天,13小时,24分17秒。
如果你确切地知道哪些字段,你会得到,你可以使用一个调用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;
纯目标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]);
这个版本解析每个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];
}
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日:增加了对周的支持
稍微修改用户的功能
谢尔盖点
+ (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];
}
下面是迅速的例子:(仅几个小时,分钟和秒)
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)
}
下面是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)
}
我抬头一看这个维基百科的文章对以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的知识搜索,这是做到这一点的最简单的方法。
快速和肮脏的实施
- (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;
}
有答案了,但我最终实现用另一个版本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;
}
现在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
}
雨燕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
}
}