Iphone 如何解析目标C中的ISO-8601持续时间?
我正在寻找一种简单的方法来解析目标C中包含ISO-8601持续时间的字符串。结果应该是可用的,比如Iphone 如何解析目标C中的ISO-8601持续时间?,iphone,objective-c,datetime,iso8601,nstimeinterval,Iphone,Objective C,Datetime,Iso8601,Nstimeinterval,我正在寻找一种简单的方法来解析目标C中包含ISO-8601持续时间的字符串。结果应该是可用的,比如NSTimeInterval ISO-8601持续时间的示例:P1DT13H24M17S,意思是1天13小时24分钟17秒。我查阅了这篇文章,以了解ISO-8601的实际工作方式。我不是可可专家,但我敢打赌,如果你能解析这个字符串并提取出成分小时、分钟、秒、天等等,那么将其转换为NSTimeInterval应该很容易。棘手的部分是解析它。我可能会这样做: NSString* iso8601 = /*
NSTimeInterval
ISO-8601持续时间的示例:P1DT13H24M17S
,意思是1天13小时24分钟17秒。我查阅了这篇文章,以了解ISO-8601的实际工作方式。我不是可可专家,但我敢打赌,如果你能解析这个字符串并提取出成分小时、分钟、秒、天等等,那么将其转换为NSTimeInterval应该很容易。棘手的部分是解析它。我可能会这样做:
NSString* iso8601 = /*However you're getting your string in*/
NSArray* iso8601Parts = [iso8601 componentsSeparatedByString:@"T"];
首先,将字符串拆分为两个单独的字符串:一个表示天数,另一个表示时间。NSString有一个实例方法ComponentSeparatedByString:NSString,它返回原始NSString的子字符串的NSArray,这些子字符串由传入的参数分隔。它看起来像这样:
NSString* iso8601 = /*However you're getting your string in*/
NSArray* iso8601Parts = [iso8601 componentsSeparatedByString:@"T"];
接下来,在ISO8601部分的第一个元素中搜索每个可能的日持续时间指标(Y、M、W和D)。找到一个数字后,抓取所有前面的数字(可能还有一个小数点),将它们转换为浮点数,并将它们存储在某个位置。请记住,如果只有时间元素,那么iso8601Parts[0]将是空字符串
然后,在ISO8601部分的第二个元素中查找可能的时间指示器(H、M、S)的时间部分,执行相同的操作。请记住,如果只有一个day组件(即原始字符串中没有“T”字符),那么ISO8601部分的长度将仅为1,尝试访问第二个元素将导致越界异常
NSTimeInterval只是一个很长的存储秒数的过程,因此将取出的各个片段转换为秒,将它们添加到一起,存储在NSTimeInterval中,就可以设置了
抱歉,我知道你要求一种“简单”的方法来做这件事,但是基于我(承认是轻松的)四处搜索和对API的了解,这是最简单的方法。如果你确切知道你将获得哪些字段,你可以使用一次调用
sscanf()
:
如果其中任何字段可能被省略,则您需要在解析过程中更聪明一些,例如:
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]);
NSString*持续时间=@“P1DT10H15M49S”;
int i=0,天=0,小时=0,分钟=0,秒=0;
while(i
此版本无错误地分析每个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];
}
答案已经有了,但我最终还是用
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的一个示例: (仅适用于小时、分钟和秒)
快速而肮脏的实现
- (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;
}
稍微修改用户的功能 谢尔盖·佩卡尔
+ (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];
}
现在进入
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
}
func convertFromISO8601Duration(isoValue:AnyObject)->字符串?{
var displayedString:String?
var hasHitTimeSection=false
var isSingular=false
如果让isoString=isoValue为?字符串{
displayedString=String()
对于isoString中的val{
如果val==“P”{
//解析“P”时不执行任何操作
持续
}如果val==“T”,则为else{
//表明我们现在正在处理ISO8601持续时间的“时间段”,然后继续。
hasHitTimeSection=true
持续
}
var tempString=String()
如果val>=“0”&&val 0&&val==“1”{
让lastIndex=count(safeDisplayedString)-1
让lastChar=safeDisplayedString[高级(safeDisplayedString.startIndex,lastIndex)]
//测试显示的字符串中当前的最后一个字符是否是空格(“”)。如果是,我们将说它是单数的,直到证明不是。
如果lastChar==“”{
isSingular=true
}否则{
isSingular=false
}
}
如果val==“1”,则为else{
//如果我们只是处理一个“1”,那么我们会说它是单数的,除非证明它是单数的。
isSingular=true
}
否则{
//…否则它是复数持续时间。
isSingular=false
}
tempString+=“\(val)”
displayedString!+=tempString
}否则{
//处理持续时间类型文本。确保正确使用月和分钟。
开关val{
案例“Y”、“Y”:
如果是单方的{
tempString+=“年”
}否则{
tempString+=“年”
}
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
}
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)
}
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
}