【问题标题】:How do I get an ISO 8601 date on iOS?如何在 iOS 上获取 ISO 8601 日期?
【发布时间】:2013-04-21 16:45:40
【问题描述】:

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

这是我发现的 long 方法:

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

对于如此重要​​的事情,这似乎是一大堆冗长的事情。

【问题讨论】:

  • 我猜空头在旁观者的眼中。三行代码很短,不是吗?您可以添加一个 C NSDate 子类,它可以用一行来完成,我刚刚被它绊倒了:github.com/soffes/SAMCategories/tree/master/SAMCategories/…。也就是说,真的,你得到了很好的答案(恕我直言),你应该奖励 Etienne 或 rmaddy 答案。它只是一种很好的做法,并奖励提供真正有用信息的人(它帮助了我,我今天需要这些信息)。与 rmaddy 相比,Etienne 可以使用更多的积分。作为一个良好措施的标志,我什至会投票赞成你的问题!

标签: ios nsdate nsdateformatter iso8601


【解决方案1】:

使用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())

【讨论】:

  • 谢谢,麦迪。在我发布我的代码之后,我现在才看到你的答案。 setLocale 重要吗?
  • @rmaddy 您可能只是编辑您的示例以使用 ZZZZZ 以便人们复制粘贴不会被烧毁。
  • @jlmendezbonini 不。你很少需要 YYYY。你通常想要 yyyy。
  • 为什么应该将语言环境设置为 en_US_POSIX 的来源:developer.apple.com/library/mac/qa/qa1480/_index.html
  • @maddy - 想让其他人通过 Google 轻松发现此内容,并感谢您实际挖掘出正确的格式、语言环境等。但很高兴将其作为单独的答案发布如果你愿意
【解决方案2】:

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
  • 运行我的答案中的三行后,string 包含“2016-09-03T21:47:27Z”。
  • 不能用这种格式处理毫秒
  • @Enrique:您应该将 NSISO8601DateFormatWithFractionalSeconds 添加到格式化程序选项中,以便能够以毫秒为单位工作,请查看文档。
  • NSISO8601DateFormatWithFractionalSeconds 仅适用于 11.2+。否则你会崩溃!
【解决方案3】:

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

(见http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns

【讨论】:

  • 伟大的收获!其他对此感兴趣的人,去链接,搜索8601就可以看到了。
  • 当日期的时区是 UTC 时,有没有办法让它显示 00:00 而不是 Z(它们是等效的)?
  • 非常感谢 ;) 用一个返回 nil 日期的日期格式化程序把我的头撞到了墙上! :)
【解决方案4】:

一个经常被忽视的问题是 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 article 关于日期格式化程序的信息。

【讨论】:

  • 能否请您说明如何在 Obj-C 中获取 NSISO8601DateFormtter? (当然在 iOS 10 及更高版本上)
【解决方案5】:

所以使用 Sam Soffee 在 NSDate 上找到的 here 类别。将该代码添加到您的项目后,您就可以在 NSDate 上使用单一方法:

- (NSString *)sam_ISO8601String

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

【讨论】:

  • 分类的当前位置是here
  • 我建议不要这样做,它有非常随机发生的内存异常问题
  • @M_Waly 你把这件事报告给他了吗?我在没有任何警告的情况下构建了他的代码,并且它很好地通过了分析检查。
  • 嗨,这里是 Sam Soffes。请不要再这样了。你应该在 Foundation 中使用ISO8601DateFormatter
【解决方案6】:

更新

从 iOS 10 开始,您可以使用 Foundation 的 NSISO8601DateFormatter

原答案

来自IS8601,问题在于表示和时区

  • ISO 8601 = year-month-day time timezone
  • 对于日期和时间,有基本格式(YYYYMMDD、hhmmss、...)和扩展格式(YYYY-MM-DD、hh:mm:ss、...)
  • 时区可以是祖鲁语、偏移量或格林威治标准时间
  • 日期和时间的分隔符可以是空格或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 标识符Date Field Symbol Table

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)

关于localeFormatting Data Using the Locale Settings

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

关于en_US_POSIXTechnical Q&A QA1480 NSDateFormatter and Internet Dates

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

有趣的相关问题

【讨论】:

  • 我刚用 2016-07-23T07:24:23Z 试过了,不行
  • @KamenDobrev “不工作”是什么意思?我只是将您的示例添加到测试中 github.com/onmyway133/ISO8601/blob/master/ISO8601Tests/… 并且它可以工作
  • 我在这个问题上链接了 ISO6801 库。请不要再这样了。你应该在 Foundation 中使用ISO8601DateFormatter
【解决方案7】:

只需使用 Foundation 框架中的 NSISO8601DateFormatter

let isoDateFormatter = ISO8601DateFormatter()
print("ISO8601 string: \(isoDateFormatter.string(from: Date()))")
// ISO8601 string: 2018-03-21T19:11:46Z

https://developer.apple.com/documentation/foundation/nsiso8601dateformatter?language=objc

【讨论】:

    【解决方案8】:

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

    -(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,则不能使用日期格式化程序,必须手动解析。
    【解决方案9】:

    使用 iOS 15,您可以获得 ISO860,如下所示:

    let iso8601String = Date.now.ISO8601Format()
    

    【讨论】:

      【解决方案10】:

      如果您是因为相反的搜索结果而出现在这里,这里是最简单的解决方案:

      let date = ISO8601DateFormatter().date(from: dateString)
      

      【讨论】:

        【解决方案11】:

        这有点简单,并将日期转换为 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()
        

        【讨论】:

          【解决方案12】:

          使用 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
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-11-12
            • 2013-05-14
            • 2013-02-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多