在iPhone上解析JSON日期

36

请原谅我,因为我对Objective C还不熟悉。

我从一个.NET网络服务获取的日期格式是 /Date(xxxxxxxxxxxxx-xxxx)/。我正在寻找如何最好地将其解析为NSDate对象的方向。我尝试对其使用dateWithTimeIntervalSince1970,但返回的是1969年的日期,而我知道它应该是2006年的日期。

寻求关于正确处理JSON日期的指导。

提前致谢!

7个回答

47

我刚为iOS 4.0+编写了此代码(因为它使用NSRegularExpression)。 它可以处理带时区偏移量和不带时区偏移量的日期。看起来运行得很好,你觉得呢?

+ (NSDate *)mfDateFromDotNetJSONString:(NSString *)string {
    static NSRegularExpression *dateRegEx = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dateRegEx = [[NSRegularExpression alloc] initWithPattern:@"^\\/date\\((-?\\d++)(?:([+-])(\\d{2})(\\d{2}))?\\)\\/$" options:NSRegularExpressionCaseInsensitive error:nil];
    });
    NSTextCheckingResult *regexResult = [dateRegEx firstMatchInString:string options:0 range:NSMakeRange(0, [string length])];

    if (regexResult) {
        // milliseconds
        NSTimeInterval seconds = [[string substringWithRange:[regexResult rangeAtIndex:1]] doubleValue] / 1000.0;
        // timezone offset
        if ([regexResult rangeAtIndex:2].location != NSNotFound) {
            NSString *sign = [string substringWithRange:[regexResult rangeAtIndex:2]];
            // hours
            seconds += [[NSString stringWithFormat:@"%@%@", sign, [string substringWithRange:[regexResult rangeAtIndex:3]]] doubleValue] * 60.0 * 60.0;
            // minutes
            seconds += [[NSString stringWithFormat:@"%@%@", sign, [string substringWithRange:[regexResult rangeAtIndex:4]]] doubleValue] * 60.0;
        }

        return [NSDate dateWithTimeIntervalSince1970:seconds];
    }
    return nil;
}

1
+1 这个方法支持1970年1月1日之前和之后的日期,不像其他使用“无符号”毫秒的方法。我的解决方案需要时区信息,所以我只是将那部分注释掉就可以了。 - J3RM
@g-ganesh 这是正则表达式。它正在解析这个格式:"/Date( xxxxxxxxxxxx-xxxx )/"。第一部分是Unix时间,第二部分是时区。首先,这里有许多双 "\",因为"\"需要在NSString对象中进行转义,然后单个"\"转义正则表达式中的内容。所以我们有 "^\/date\(",它只匹配字符串开头的"/date("。然后我们有 "(-?\d++)",它捕获了可选的负秒数,并至少有两个数字(我不记得为什么必须有至少两个数字)。 - jasongregori
@motionpotion 你应该得到了 "Wed, 29 May 2013 18:21:31 GMT"。你得到了什么?你有考虑时区吗? - jasongregori
不错的回答,但是它会在空值时引发异常,可以在开头加一个检查:if ((NSNull *)string == [NSNull null]) return nil; - nevan king
对我来说无法使用时区。例如,"/Date(1414796400000+0100)/" 返回一个NSDate 2014-11-01 01:00:00 CET(+01.00),这与epochconverter.com上的结果不符。必须取消注释有关时区的部分。 - Zeezer
显示剩余7条评论

18

我在使用不支持日期格式的json-framework时遇到了同样的问题,因为它不是官方JSON。我的数据源来自使用JSON.Net构建的API。这是我想出来的解决方法:

- (NSDate*) getDateFromJSON:(NSString *)dateString
{
    // Expect date in this format "/Date(1268123281843)/"
    int startPos = [dateString rangeOfString:@"("].location+1;
    int endPos = [dateString rangeOfString:@")"].location;
    NSRange range = NSMakeRange(startPos,endPos-startPos);
    unsigned long long milliseconds = [[dateString substringWithRange:range] longLongValue];
    NSLog(@"%llu",milliseconds);
    NSTimeInterval interval = milliseconds/1000;
    return [NSDate dateWithTimeIntervalSince1970:interval];
}

我并没有像上面的回答那样处理日期格式中的附加部分,所以我没有进行过处理。同时我也没有进行错误捕获,这对我来说都是新的。


这个方法有一个缺陷,它只支持1970年1月1日之后的日期时间。在1970年1月1日之前的日期将返回非常错误的日期。造成这种情况的原因是毫秒数是无符号的,这意味着它只支持正数,所以任何负数都会导致不可预期的结果。 - J3RM
2
感谢你的负评。如果你仔细阅读问题,它说他们正在寻找“一些方向”,而不是每个可能的日期格式的防弹答案。一个评论可能已经足够了,但无论如何都可以。因为实际上做出有用的回答而被踩,这让我想帮助其他人。 - toxaq

11

我之前发现使用NSRegularExpression的代码片段非常有用,不过后来我想到了另一个解决方案,它使用NSCharecterSet来去除毫秒。

+ (NSDate*) dateFromJSONString:(NSString *)dateString
{
    NSCharacterSet *charactersToRemove = [[ NSCharacterSet decimalDigitCharacterSet ] invertedSet ];
    NSString* milliseconds = [dateString stringByTrimmingCharactersInSet:charactersToRemove];   

    if (milliseconds != nil && ![milliseconds isEqualToString:@"62135596800000"]) {
        NSTimeInterval  seconds = [milliseconds doubleValue] / 1000;
        return [NSDate dateWithTimeIntervalSince1970:seconds];
    }
    return nil;
}

节省大量手动字符串处理,使代码更加简洁。


这是一个非常好的解决方案。只是想知道为什么没有人点赞。谢谢。 - Henry Gao
这不适用于纪元前的日期,因为它还会剥离任何符号字符。 - Niels

9
作为一名学习Objective-C的.NET程序员,当我尝试使用.NET WebService时,遇到了同样的问题。
起初,我以为我可以使用NSDateFormatter...我在这里找到了一个非常好的参考资料,它的符号在这里,但我很快意识到我需要将毫秒数转换为秒数。
我写了代码来实现它...我仍在学习Obj-C,但我认为这不应该那么难...
- (NSDate *) getJSONDate{
    NSString* header = @"/Date(";
    uint headerLength = [header length];

    NSString*  timestampString;

    NSScanner* scanner = [[NSScanner alloc] initWithString:self];
    [scanner setScanLocation:headerLength];
    [scanner scanUpToString:@")" intoString:&timestampString];

    NSCharacterSet* timezoneDelimiter = [NSCharacterSet characterSetWithCharactersInString:@"+-"];
    NSRange rangeOfTimezoneSymbol = [timestampString rangeOfCharacterFromSet:timezoneDelimiter];

    [scanner dealloc];

    if (rangeOfTimezoneSymbol.length!=0) {
        scanner = [[NSScanner alloc] initWithString:timestampString];

        NSRange rangeOfFirstNumber;
        rangeOfFirstNumber.location = 0;
        rangeOfFirstNumber.length = rangeOfTimezoneSymbol.location;

        NSRange rangeOfSecondNumber;
        rangeOfSecondNumber.location = rangeOfTimezoneSymbol.location + 1;
        rangeOfSecondNumber.length = [timestampString length] - rangeOfSecondNumber.location;

        NSString* firstNumberString = [timestampString substringWithRange:rangeOfFirstNumber];
        NSString* secondNumberString = [timestampString substringWithRange:rangeOfSecondNumber];

        unsigned long long firstNumber = [firstNumberString longLongValue];
        uint secondNumber = [secondNumberString intValue];

         NSTimeInterval interval = firstNumber/1000;

        return [NSDate dateWithTimeIntervalSince1970:interval];
    }

    unsigned long long firstNumber = [timestampString longLongValue];
    NSTimeInterval interval = firstNumber/1000;

    return [NSDate dateWithTimeIntervalSince1970:interval];
}

希望有人能提供更好的 Obj-C 解决方案。如果没有,我可能会保留这个解决方案或寻找在 .NET 中更改序列化格式的方法。

编辑:

关于 JSON DateTime 格式...... 如果您可以控制服务,则最好将日期转换为字符串放在 DataContract 对象中。

格式化为 RFC1123 现在对我来说似乎是一个不错的想法。因为我可以轻松地使用 NSDateFormatter 进行格式化。

Rick Strahl 的引用:

JavaScript 没有日期文字,Microsoft 设计了一种定制的日期格式,它实际上是一个标记化的字符串。该格式是一个被编码的字符串,包含标准的 new Date(milliseconds since 1970) 值。


谢谢你的回答。我基本上采用了与你相同的方法。感觉像是一个hack,我希望有一个更干净的解决方案,但现在它能够工作。 - user213517
1
[scanner dealloc]; 应该是 [scanner release]; !! :) - Abduliam Rehmanius
不错的解决方案,但是在解析后 secondNumber 没有被使用。应该对 GMT 的日期进行修正。 - ajonnet

5
理论:微软将C# DateTime在JSON中编码为自1970年以来的毫秒数。 解决方案:
NSString*
    dateAsString = @"/Date(1353720343336+0000)/";
    dateAsString = [dateAsString stringByReplacingOccurrencesOfString:@"/Date("
                                                           withString:@""];
    dateAsString = [dateAsString stringByReplacingOccurrencesOfString:@"+0000)/"
                                                           withString:@""];

unsigned long long milliseconds = [dateAsString longLongValue];
NSTimeInterval interval = milliseconds/1000;
NSDate* date = [NSDate dateWithTimeIntervalSince1970:interval];

这是我能想到的最简单的解决方案。

这不是假设时间为UTC吗? - mkral

0

2
你能否请提供一个如何做这件事的示例? - odyth
1
我必须说,在戴夫提供如何实现此操作的代码之前,这是错误的。Json日期是自特定日期和时间以来的秒数。那么设置格式如何返回正确的NSDate呢?文档链接中没有任何关于如何将格式设置为不同范例(除了MM DD YY样式)的参考资料。 - J3RM

0

-(NSString*)convertToUTCTime:(NSString*)strDate{

-(NSString*)convertToUTCTime:(NSString*)strDate{

NSDate *currentDate = [NSDate date];
myDate = [commonDateFormatter dateFromString: strDate];
NSTimeInterval distanceBetweenDates = [currentDate timeIntervalSinceDate:myDate];
return [self stringFromTimeInterval:distanceBetweenDates];

} - (NSString *)stringFromTimeInterval:(NSTimeInterval)interval { NSInteger ti = (NSInteger)interval; NSInteger minutes = (ti / 60) % 60; NSInteger hours = (ti / 3600);

if (hours > 24) {
    NSInteger days = hours/24;
    if (days > 30) {
       NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
       [dateFormatter setDateFormat:@"EEE d MMM, h:mm a"];
       //[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"IST"]];
        NSString *daydate = [dateFormatter stringFromDate:myDate];
        return daydate;
    }
    else{
    return [NSString stringWithFormat:@" %2ldd",(long)days];
    }
}else{
    if (hours == 0 && minutes < 1) {
        return [NSString stringWithFormat:@"Today"];
    }
    else if (hours == 0 && minutes < 60){
        return [NSString stringWithFormat:@"%2ldm ",(long)minutes];
    }
    else{
    return [NSString stringWithFormat:@" %2ldh",(long)hours];
    }
}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接