如何在iOS上获得ISO 8601日期?


115

2004-02-12T15:19:21+00:00通过date('c'),在PHP中获取ISO 8601日期字符串(例如)非常容易,但是如何在Objective-C(iPhone)中获取它呢?有没有类似的短途方法?

这是我发现可以做的很长的路要走:

NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDate *now = [NSDate date];
NSString *formattedDateString = [dateFormatter stringFromDate:now];
NSLog(@"ISO-8601 date: %@", formattedDateString);

// Output: ISO-8601 date: 2013-04-27T13:27:50-0700

对于如此重要​​的事情,似乎有很多困难。


1
我猜情人眼中的情节短。三行代码很短,不是吗?您可以添加一个C NSDate子类,只需一行就可以完成,我刚刚对其进行了调试github.com/soffes/SAMCategories/tree/master/SAMCategories/…。就是说,确实,您得到了很好的答案(IMHO),您应该将答案授予Etienne或rmaddy。它只是一种很好的做法,可以奖励提供真正有用信息的人(它对我有帮助,我今天需要此信息)。艾蒂安(Etienne)可以比rmaddy多使用积分。作为一种好的措施,我什至会投票赞成您的问题!
David H

Answers:


212

用途NSDateFormatter

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];   
NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
[dateFormatter setCalendar:[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]];

NSDate *now = [NSDate date];
NSString *iso8601String = [dateFormatter stringFromDate:now];

在Swift中:

let dateFormatter = DateFormatter()
let enUSPosixLocale = Locale(identifier: "en_US_POSIX")
dateFormatter.locale = enUSPosixLocale
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.calendar = Calendar(identifier: .gregorian)

let iso8601String = dateFormatter.string(from: Date())

1
谢谢,妈妈。发布代码后,我现在才看到您的答案。是setLocale重要的?
JohnK

2
@rmaddy您可能只是编辑您的示例以使用ZZZZZ,以免复制粘贴的人被烫伤。
杰西·鲁萨克

4
@jlmendezbonini不。您要YYYY非常罕见。您通常需要yyyy。
rmaddy14年

7
为何将语言环境设置为en_US_POSIX的来源:developer.apple.com/library/mac/qa/qa1480/_index.html
2015年

11
@maddy -想让它容易发现他人通过谷歌找到这一点,并想给你的信贷实际上挖格式正确,语言环境等。但高兴地张贴作为一个单独的答案,如果你想
罗宾

60

iOS 10引入了一个新NSISO8601DateFormatter类来处理此问题。如果您使用的是Swift 3,则您的代码将如下所示:

let formatter = ISO8601DateFormatter()
let date = formatter.date(from: "2016-08-26T12:39:00Z")
let string = formatter.string(from: Date())

它给出什么格式?同样也很高兴知道它是否可以处理以下内容:2016-08-26T12:39:00-0000和2016-08-26T12:39:00-00:00和2016-08-26T12:39:00.374-0000
malhal's

1
运行我的答案中的三行后,string包含“ 2016-09-03T21:47:27Z”。
TwoStraws

3
CAN NOT处理毫秒这一格式。
Enrique

3
@Enrique:您应该将NSISO8601DateFormatWithFractionalSeconds添加到格式化程序选项中,以便能够使用毫秒,请检查文档。
PakitoV

1
NSISO8601DateFormatWithFractionalSeconds仅适用于11.2+。否则,您将当机!
hash3r

27

作为对maddy答案的补充,对于ISO 8601,时区格式应为“ ZZZZZ”(Z的5倍),而不是单个“ Z”(对于RFC 822格式)。至少在iOS 6上。

(请参阅http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns


很棒!任何对此感兴趣的人,请转到链接,搜索8601,您将看到它。
David H

当日期的时区为UTC时,有没有办法让它显示00:00而不是Z(等价)?
shim

非常感谢;)用返回nil date的dateformatter撞到了墙上!:)
polo987 '16

19

一个经常被忽略的问题是ISO 8601格式的字符串可能有毫秒,也可能没有毫秒。

换句话说,“ 2016-12-31T23:59:59.9999999”和“ 2016-12-01T00:00:00”都是合法的,但是如果您使用的是静态类型的日期格式化程序,则不会解析其中之一。

iOS 10开始,您应该使用ISO8601DateFormatter它来处理ISO 8601日期字符串的所有变体。请参见下面的示例:

let date = Date()
var string: String

let formatter = ISO8601DateFormatter()
string = formatter.string(from: date)

let GMT = TimeZone(abbreviation: "GMT")
let options: ISO8601DateFormatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
string = ISO8601DateFormatter.string(from: date, timeZone: GMT, formatOptions: options)

对于iOS 9及以下版本,请对多个数据格式化程序使用以下方法。

我还没有找到一个涵盖这两种情况并能抽象出这种细微差别的答案。这是解决该问题的解决方案:

extension DateFormatter {

    static let iso8601DateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static let iso8601WithoutMillisecondsDateFormatter: DateFormatter = {
        let enUSPOSIXLocale = Locale(identifier: "en_US_POSIX")
        let iso8601DateFormatter = DateFormatter()
        iso8601DateFormatter.locale = enUSPOSIXLocale
        iso8601DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
        iso8601DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
        return iso8601DateFormatter
    }()

    static func date(fromISO8601String string: String) -> Date? {
        if let dateWithMilliseconds = iso8601DateFormatter.date(from: string) {
            return dateWithMilliseconds
        }

        if let dateWithoutMilliseconds = iso8601WithoutMillisecondsDateFormatter.date(from: string) {
            return dateWithoutMilliseconds
        }

        return nil
    }
}

用法:

let dateToString = "2016-12-31T23:59:59.9999999"
let dateTo = DateFormatter.date(fromISO8601String: dateToString)
// dateTo: 2016-12-31 23:59:59 +0000

let dateFromString = "2016-12-01T00:00:00"
let dateFrom = DateFormatter.date(fromISO8601String: dateFromString)
// dateFrom: 2016-12-01 00:00:00 +0000

我还建议查看有关日期格式化程序的Apple文章


您能否也说明如何在Obj-C中获得NSISO8601DateFormtter?(当然,在iOS 10及更高版本上)
Motti Shneor

8

因此,请在此处使用NSDate上的Sam Soffee的类别。将该代码添加到您的项目后,您便可以在NSDate上使用一种方法:

- (NSString *)sam_ISO8601String

它不仅是一行,而且比NSDateFormatter方法要快得多,因为它是用纯C语言编写的。


1
该类别的当前位置在这里
Perry

1
我建议不
要这样做

1
@M_Waly您向他报告了吗?我在没有警告的情况下构建了他的代码,并且它通过了分析检查。
David H

6

IS8601开始,问题在于表示形式和时区

  • ISO 8601 = year-month-day time timezone
  • 对于日期和时间,有基本格式(YYYYMMDD,hhmmss,...)和扩展格式(YYYY-MM-DD,hh:mm:ss,...)
  • 时区可以是Zulu,offset或GMT
  • 日期和时间的分隔符可以是空格,或者 T
  • 日期有星期格式,但很少使用
  • 时区之后可能有很多空格
  • 第二个是可选的

这是一些有效的字符串

2016-04-08T10:25:30Z
2016-04-08 11:25:30+0100
2016-04-08 202530GMT+1000
20160408 08:25:30-02:00
2016-04-08 11:25:30     +0100

解决方案

NSDateFormatter

所以这是我在onmyway133 ISO8601中使用的格式

let formatter = NSDateFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyyMMdd HHmmssZ"

关于Z标识符日期字段符号表

Z: The ISO8601 basic format with hours, minutes and optional seconds fields. The format is equivalent to RFC 822 zone format (when optional seconds field is absent)

关于locale 使用语言环境设置格式化数据

语言环境代表特定用户的格式选择,而不是用户的首选语言。这些通常是相同的,但可以不同。例如,居住在德国的以英语为母语的人可能选择英语作为语言,选择德国作为区域

关于en_US_POSIX 技术问答QA1480 NSDateFormatter和Internet日期

另一方面,如果要使用固定格式的日期,则应首先将日期格式器的语言环境设置为适合您的固定格式的语言环境。在大多数情况下,最佳选择的语言环境是“ en_US_POSIX”,该语言环境专门设计用于产生美式英语结果,而与用户和系统偏好无关。“ en_US_POSIX”的时间也不变(如果美国在将来某个时候改变其日期格式的方式,“ en_US”将更改以反映新的行为,但“ en_US_POSIX”不会改变)以及机器之间( “ en_US_POSIX”在iOS上的工作原理与在OS X上以及其他平台上的工作原理相同。

有趣的相关风俗


我刚刚尝试使用2016-07-23T07:24:23Z,它不起作用
Kamen Dobrev

@KamenDobrev“不工作”是什么意思?我只是将您的示例添加到测试github.com/onmyway133/ISO8601/blob/master/ISO8601Tests/…,它可以正常工作
onmyway133 '16


3

基于以下要点:https : //github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift,可以使用以下方法将yyyy-MM格式的NSDate转换为ISO 8601日期字符串-dd'T'HH:mm:ss.SSSZ

-(NSString *)getISO8601String
    {
        static NSDateFormatter *formatter = nil;
        if (!formatter)
        {
            formatter = [[NSDateFormatter alloc] init];
            [formatter setLocale: [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"UTC"];
            [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];

        }
        NSString *iso8601String = [formatter stringFromDate: self];
        return [iso8601String stringByAppendingString: @"Z"];
    }

S为子二是这里的关键
马克·迈尔

请注意,如果您需要超过3 S,则无法使用日期格式程序必须手动对其进行解析。
malhal's

-1

这有点简单,并将日期放入UTC。

extension NSDate
{
    func iso8601() -> String
    {
        let dateFormatter = NSDateFormatter()
        dateFormatter.timeZone = NSTimeZone(name: "UTC")
        let iso8601String = dateFormatter.stringFromDate(NSDate())
        return iso8601String
    }
}

let date = NSDate().iso8601()

-1

使用Swift 3.0和iOS 9.0

extension Date {
    private static let jsonDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "UTC")!
        return formatter
    }()

    var IOS8601String: String {
        get {
            return Date.jsonDateFormatter.string(from: self)
        }
    }

    init?(fromIOS8601 dateString: String) {
        if let d = Date.jsonDateFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since:d)
        } else {
            return nil
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.