处理日期的常见情景

  1. NSDate -> String & String -> NSDate

  2. 日期比较

  3. 日期计算(基于参考日期 +/- 一定时间)

  4. 计算日期间的差异

  5. 拆解NSDate对象(分解成year/month/day/hour/minute/second 等)

NSDate相关类

  1. NSDate

  2. DateFormatter

  3. DateComponents

  4. DateComponentFormatter

  5. Calendar

  6. Date structure: Swift3.0中引入了Date structure, 和NSDate提供的功能相似, 并且Date结构体和NSDate类可以在Swift中交替使用以此达到和Objective-C APIs的交互. Date结构体的引入和Swift中引入了String, Array等类型一样, 都是为了让对应的类桥接Foundation中对应的类(String -> NSString, Array -> NSArray)

注: 在下面的代码片段中, NSDate和Date会交替使用,功能都是相同的.

基本概念

  • 在具体开始写代码之前, 搞清楚一些基本的概念是十分必要的:

    • NSDate对象: 同时可以描述日期和时间, 当要处理日期或者时间时会使用到.

    • DateFormatter对象: 格式对象只要在将NSDate和String相互转换的时候才有价值, 它是用来规定格式的. 包括系统自带的格式和手动自定义的格式,同时该类也支持时区的设置.

    • DateComponents类: 可以看做是NSDate的姊妹类. 因为它提供了很多实用的特性和操作. 最重要的一个特性就是它可以把日期或者时间拆解开来, 即日期或者时间里的每一个部分(比如年,月,日,小时,分钟等)都可以单独取出来,并且进行其他的操作(比如计算).

    • DateComponentsFormatter类: 用来将计算机读入的日期和时间输出为了人类可读的字符串.

    • Calendar类: 日期类可以实现NSDate和DateComponents之间的转换.

NSDate和String之间的转换

  • 获得当前的日期和时间

    let currentDate = Date()
    print(currentDate) // 2016-08-19 05:33:48 +0000 格林威治时间

      

  • 初始化DateFormatter类

    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale.current() // 设置时区

      

  • 使用系统自带样式输出日期

    • 在将NSDate对象转换成String类型前, 首先需要告诉计算机你想要输出什么样的日期格式. 这里有两种方式. 第一就是使用系统自带的格式, 第二个方法是手动使用特定的说明符来指定输出格式.这里先看系统自带格式的输出.

      dateFormatter.dateStyle = DateFormatter.Style.noStyle
      var stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // "无输出" dateFormatter.dateStyle = DateFormatter.Style.shortStyle
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // 8/19/16 dateFormatter.dateStyle = DateFormatter.Style.mediumStyle
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // Aug 19, 2016 dateFormatter.dateStyle = DateFormatter.Style.longStyle
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // August 19, 2016 dateFormatter.dateStyle = DateFormatter.Style.fullStyle
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // Friday, August 19, 2016

        

  • 使用自定义说明符输出日期

    • EEEE: 代表一天的全名,比如Monday.使用1-3个E就代表简写,比如Mon.

    • MMMM: 代表一个月的全名,比如July.使用1-3个M就代表简写,比如Jul.

    • dd: 代表一个月里的几号,比如07或者30.

    • yyyy: 代表4个数字表示的年份,比如2016.

    • HH: 代表2个数字表示的小时,比如08或17.

    • mm: 代表2个数字表示的分钟,比如01或59.

    • ss: 代表2个数字表示的秒,比如2016.

    • zzz: 代表3个字母表示的时区,比如GTM(格林尼治标准时间,GMT+8为北京所在的时区,俗称东八区)

    • GGG: BC或者AD, 即公元前或者公元

    • 系统自带的样式不够用时, 就可以使用自定义说明符自定义Date的输出格式.

    • 自定义说明符的另一个巨大的作用就是可以将复杂的字符类型的日期格式(比如Fri, 08 Aug 2016 09:22:33 GMT)转换成Date类型.

    • 自定义格式的使用最重要的就是自定义说明符的使用,说明符是一些对日期对象有特点含义的简单的字符.下面首先列举一些这里会用到的说明符:

    • 关于说明符的具体介绍,请参照官方文档

    • 继续来看自定义说明符的实际使用, 下面将现在的日期转换成字符串类型, 并且输出星期和月份的全名, 年份和天数用数字表示:

      dateFormatter.dateFormat = "EEEE, MMMM, dd, yyyy"
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // Friday, August, 19, 2016

        

    • 从例子中可以很直观的看到其实自定义输出格式的技术很简单, 具体的输出格式根据具体的需求而定, 最后再举一个例子(输出格式--> 小时:分钟:秒 星期简写 月份显示数字 天数显示数字 时区 公元):

      dateFormatter.dateFormat = "HH:mm:ss E M dd zzz GGG"
      stringDate = dateFormatter.string(from: currentDate)
      print(stringDate) // 14:20:31 Fri 8 19 GMT+8 AD

        

  • 上面例子全部是Date转String, 这个转换过程的逆转换更加有趣. 之前用到的系统自带的输出格式和自定义的说明符在String转Date的过程中同样适用. String转Date过程中最重要的一点就是要设置合适的格式对应与String, 否则输出会是nil.

    var dateString = "2016-12-02 18:15:59"
    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    print(dateFormatter.date(from: dateString)) // 2016-12-02 10:15:59 +0000

      

    • 这里需要注意的是字符串的时间是18:15:59, 而输出的时间是10:15:59, 原因是因为我所在的时区是北京所在的时区,即东八区,而默认输出是按格林尼治标准时间输出的,即相差8小时.

    • 下面举一个更复杂的例子, 包括时区的输出:

      dateString = "Mon, 08, Aug 2008 20:00:01 GMT"
      dateFormatter.dateFormat = "E, dd, MM yyyy HH:mm:ss zzz"
      dateFromString = dateFormatter.date(from: dateString)
      print(dateFromString) // 2008-08-08 20:00:01 +0000

        

DateComponents

  • 在很多情景中,你可能需要将日期的值进行拆解,进而提取其中特定的值.比如你可能需要得到一个日期中天数和月份的值,或者从时间里面得到小时和分钟的值,这部分将会介绍DateComponents类以此来解决这个问题. 需要注意的是,DateComponents类会经常和Calendar搭配使用,具体来讲,Calendar类的方法是真正实现了Date到DateComponents的转换,以及逆转换.记住这一点之后,首先先得到currentCalendar对象,并且将它赋值给常量以便于后面的使用.

    let calendar = Calendar.current()
    

      

  • 下面的代码通过一个典型的例子演示一遍Date -> DateComponents的过程.

    let dateComponents = calendar.components([Calendar.Unit.era, Calendar.Unit.year,    Calendar.Unit.month, Calendar.Unit.day, Calendar.Unit.hour, Calendar.Unit.minute,   Calendar.Unit.second], from: currentDate)
    print("era:(dateComponents.era) year:(dateComponents.year) month:(dateComponents.month) day: (dateComponents.day) hour:(dateComponents.hour) minute:(dateComponents.minute) second: (dateComponents.second)")
    // era:Optional(1) year:Optional(2016) month:Optional(8) day:Optional(19) hour:Optional(15) minute:Optional(29) second:Optional(13)

      

    • 上面用到了Calendar类中的components(_:from:)方法. 这个方法接收两个参数, 第二个传入的参数是将要被拆解的日期对象,第一个参数比较有意思, 这个参数是一个数组,里面放入组成日期的各个成分单位(Calendar.Unit),比如月(Calendar.Unit.month), 日(Calendar.Unit.day).

    • Calendar.Unit是一个结构体, 它里面的所有属性及说明可以在官方文档中查看.

    • 还需要注意的一点就是在components(_:from:)方法的第一个数组参数中,如果没有传入想要解析的单位名称,之后从DateComponents对象中是得不到这个单位的, 比如上面的方法中没有传入Calendar.Unit.month, 那么最后打印的时候如果也打印了该值, 得到的值会是nil.

      dateComponents = calendar.components([Calendar.Unit.year], from: currentDate)
      print("year:(dateComponents.year) month:(dateComponents.month)")
      // Optional(2016) month:nil

        

  • DateComponents -> Date

    • DateComponents -> Date的转换也十分简单, 只需要初始化一个DateComponents对象, 然后指定特定的components, 最后调用Calendar类的dateFromComponents:方法完成转换

      var components = DateComponents()
      components.year = 1985
      components.month = 02
      components.day = 05
      components.hour = 07
      components.minute = 08
      components.second = 44
      let dateFromComponents = calendar.date(from: components)
      print(dateFromComponents) // Optional(1985-02-04 23:08:44 +0000)

        

  • 这里同样可以设置不同的时区来得到当地的时间:

    components.timeZone = TimeZone(abbreviation: "GMT") // Greenwich Mean Time
    components.timeZone = TimeZone(abbreviation: "CST") // China Standard Time
    components.timeZone = TimeZone(abbreviation: "CET") // Central European Time

      

比较日期和时间

  • 除了以上提到的应用场景, 另一个关于日期和时间常见的应用场景就是比较. 通过对两个Date对象的比较以此来判断哪个日期更早或者更迟,或者是否日期相同. 这里将会介绍3种方法来做比较, 方法不分好坏, 适合自己的需求最重要.

  • 在开始进行比较之前, 先创建两个Date对象用于下面的比较:

    dateFormatter.dateFormat = "MMM dd, yyyy zzz"
    dateString = "May 08, 2016 GMT"
    var date1 = dateFormatter.date(from: dateString) dateString = "May 10, 2016 GMT"
    var date2 = dateFormatter.date(from: dateString) print("date1:(date1)----date2:(date2)")
    // date1:Optional(2016-05-08 00:00:00 +0000)
    // date2:Optional(2016-05-10 00:00:00 +0000)

      

方法1 (earlierDate: || laterDate:)

  • 当比较date1和date2两个日期哪个更早或更晚时, 使用NSDate类提供的earlierDate:laterDate:方法就可以实现.

    print((date1! as NSDate).earlierDate(date2!)) // 2016-05-08 00:00:00 +0000
    print((date1! as NSDate).laterDate(date2!)) // 2016-05-10 00:00:00 +0000

      

    • 当使用earlierDate:方法时, 返回值是日期更早的NSDate对象; laterDate:的返回值是日期更晚的NSDate对象.

    • 上面的方法中, 因为date1和date2属于Date类,而earlierDate:laterDate:方法想要接收的参数类型是NSDate, 所以先进行了类型转换.

方法2 (compare: )

  • 第二个比较的方法是使用Date结构体提供的compare:方法, 返回值是ComparisonResult类型的枚举.

    if date1?.compare(date2!) == ComparisonResult.orderedAscending {
    print("date1 is earlier")
    } else if date1?.compare(date2!) == ComparisonResult.orderedDescending {
    print("date2 is earlier")
    } else if date1?.compare(date2!) == ComparisonResult.orderedSame {
    print("Same date!!!")
    }

      

方法3 (timeIntervalSinceReferenceDate)

  • 第三个方法有点不同, 原理是分别将date1 和 date2 与一个参考日期进行比对, 然后通过判断两个日期和参考日期的差距, 进而判断两个日期的差距.

  • 举一个例子: 比较4和10两个数字, 先选取6作为一个参考数字, 4-6=-2;10-6=4,4-(-2)=6.

    if date1?.timeIntervalSinceReferenceDate > date2?.timeIntervalSinceReferenceDate {
    print("date1 is later")
    } else if date1?.timeIntervalSinceReferenceDate < date2?.timeIntervalSinceReferenceDate {
    print("date2 is later")
    } else if date1?.timeIntervalSinceReferenceDate == date2?.timeIntervalSinceReferenceDate {
    print("Same Date")
    }

      

日期的计算

  • 处理日期的另一个有趣的场景就是日期的计算. 比如计算2016-08-12号加2个月零12天是多少诸如此类的计算. 这里将介绍两种不同的方法来进行日期计算. 第一个方法使用了Calendar类的CalendarUnit结构体; 第二个方法使用到了DateComponents类.

  • 首先记住当前的日期:

    print(NSDate()) // 2016-08-19 08:29:12 +0000
    

      

  • 下面假设想计算当前日期再加2个月零5天

    let monthsToAdd = 2
    let daysToAdd = 5

      

方法1

var calculatedDate = calendar.date(byAdding: Calendar.Unit.month, value: monthsToAdd, to:   currentDate, options: Calendar.Options.init(rawValue: 0))
calculatedDate = calendar.date(byAdding: Calendar.Unit.day, value: daysToAdd, to: calculatedDate!, options: Calendar.Options.init(rawValue: 0))
print(calculatedDate) // Optional(2016-10-24 08:33:41 +0000)

  

  • 如上面代码所示, 这里使用了dateByAddingUnit:value:toDate:options:方法. 这个方法就是将特定的日期单位值加到现有的日期值上, 然后返回一个新的Date对象. 因为这里的例子要加两个值, 所以需要调用该方法两次.

方法2

  • 方法1虽然可行, 但有缺点, 即每一个单位的计算都需要分别调用方法, 假如想给当前日期加上7年3个月4天8小时23分钟34秒, 那么就要调用dateByAddingUnit:value:toDate:options:方法6次! 而方法2则可以对这个问题迎刃而解. 而方法2有一次使用到了DateComponents这个类.

  • 下面我将首先初始化一个新的DateComponents对象, 然后设置月份和天数, 然后再调用Calendar类中名为dateByAddingComponents:toDate:options:的方法, 该方法的返回值就是计算好的Date对象.

    var newDateComponents = DateComponents()
    newDateComponents.month = 2
    newDateComponents.day = 5
    calculatedDate = calendar.date(byAdding: newDateComponents, to: currentDate, options: Calendar.Options.init(rawValue: 0))
    print(calculatedDate) // Optional(2016-10-24 08:47:57 +0000)

      

日期差值的计算

  • 计算两个日期间具体的差值也是在处理日期对象时经常会遇到的问题. 这里将介绍3中方法来解决该问题.

  • 首先, 先自定义两个日期对象:

    dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    dateString = "2016-08-08 18:25:17"
    date1 = dateFormatter.date(from: dateString) dateString = "1985-02-05 07:45:38"
    date2 = dateFormatter.date(from: dateString)

      

方法1

  • 方法1用到的是Calendar类中的一个对象方法components:from:to:options:.

    var diffComponents = calendar.components([Calendar.Unit.year, Calendar.Unit.month,  Calendar.Unit.day], from: date2!, to: date1!, options: Calendar.Options.init(rawValue: 0))
    
    print("date1 and date2 are different in (diffComponents.year)years    (diffComponents.month)months and (diffComponents.year)days")
    // date1 and date2 are different in Optional(31)years Optional(6)months and Optional(31)days

      

    • components:from:to:options:方法中的第一个参数还是接收一个包含了Calendar.Unit结构体的数组. 因为是从date2比date1,升序的返回值就是正数,如果是反过来比,返回值就是负数.

方法2

  • 这里介绍的第二个方法将会第一次使用到DateComponentsFormatter类. DateComponentsFormatter类提供了很多不同的方法进行日期间自动的计算,并且返回值是格式化的字符串.

  • 首先初始化一个DateComponentsFormatter对象, 然后先指定它的一个属性值.

    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full

      

    let interval = date2?.timeIntervalSince(date1!)
    var diffString = dateComponentsFormatter.string(from: interval!)
    print(diffString) // Optional("-31 years, 6 months, 0 weeks, 3 days, 10 hours, 39 minutes, 39 seconds")

      

    • UnitsStyle属性会指定最后结果输出的格式. 这里使用full,就代表结果输出的单位会全名显示,如Monday,June等.如果想用简写,就可以重新设置属性.官方文档中列出了所有的属性值。

方法3

dateComponentsFormatter.allowedUnits = [Calendar.Unit.year]
diffString = dateComponentsFormatter.string(from: date1!, to: date2!)
print(diffString) // Optional("-31 years")

  

  • 最后一个方法调用了stringFrom:to:方法来计算. 注意使用该方法之前, 必须至少在allowedUnits属性中设置一个calendar unit, 否则这个方法将会返回nil, 所以在使用该方法之前, 先指定想要怎么样看到输出, 然后再让执行输出的方法。

总结

就像我在摘要中说的, 处理日期很常见, 在日常代码中也不可避免, 这里我通过一些小的代码片段介绍了处理日期的一些常见方法. 不管是NSDate类,Date结构体还是与之相关联的类, 它们的目的只有一个, 就是能够快速的处理日期. 如果想深入掌握有关日期的处理, 还是要在日常编码过程中多多练习, 多多阅读官方文档

学习收藏, 转自:

Swift3.0中关于日期类的使用指引

其他开发过程中遇到的问题,后续将进行补充。。。

iOS 日期处理 (Swift3.0 NSDate)的更多相关文章

  1. Swift3.0中关于日期类的使用指引

    日期的处理在大大小小的iOS项目中都十分常见,随着Swift3.0正式版的即将推出,语法的改变让NSDate以及相关类的使用都与之前略有不同,这里将会对基于Swift3.0版本的NSDate及相关类的 ...

  2. Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)

    本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...

  3. Swift3.0 iOS获取当前时间 - 年月日时分秒星期

    Swift3.0 iOS获取当前时间 - 年月日时分秒星期func getTimes() -> [Int] { var timers: [Int] = [] // 返回的数组 let calen ...

  4. Swift3.0服务端开发(五) 记事本的开发(iOS端+服务端)

    前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架.本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在<Swift3.0服务端开发(一)>这篇 ...

  5. iOS开发 swift3.0中文版

    swift3.0中文版: http://pan.baidu.com/s/1nuHqrBb

  6. iOS开发之资讯类App常用分类控件的封装与实现(CollectionView+Swift3.0+)

    今天博客中,我们就来实现一下一些常用资讯类App中常用的分类选择的控件的封装.本篇博客中没有使用到什么新的技术点,如果非得说用到了什么新的技术点的话,那么勉强的说,用到了一些iOS9以后UIColle ...

  7. ios日期格式转换

    转自:http://blog.csdn.net/l_ch_g/article/details/8217725 1.如何如何将一个字符串如“ 20110826134106”装化为任意的日期时间格式,下面 ...

  8. Swift3.0都有哪些变化

    从写第一篇Swift文章的时候到现在Swift已经从1.2发展到了今天的3.0,这期间由于Swift目前还在发展阶段并不能向下兼容,因此第一篇文章中的部分代码在当前的Xcode环境中已经无法运行.在W ...

  9. swift3.0的改变

    Swift在这2年的时间内,发展势头迅猛,在它开源后,更是如井喷一样,除了 iOS.mac 平台,还支持了 Linux. 而今年下半年, Swift 3.0 也会随之发布.https://github ...

随机推荐

  1. SQLSERVER 2012之AlwaysOn -- 一次硬件升级引发的问题

    这是上周遇到的一个案例:对已有的硬件进行升级而引发的问题,期间还触发了一个比较严重的BUG,可谓多灾多难:不过值得庆幸的是,在一连串连锁问题出现的时候,并没有出现人工操作失误(这往往是在处理故障中风险 ...

  2. 在ASP.NET 5中显示错误信息

    在 ASP.NET 5 中如果不进行显示错误信息的相关配置,在发生错误时,在浏览器中只能看到空白页面. 显示错误信息的配置方法如下: 1)在 project.json 中添加对 Microsoft.A ...

  3. 切身体验苹果Reminders的贴心设计

    今天吃晚饭时在iPhone的Reminders上添加了一个任务并且设定了时间. 回来后忘了这个任务,在iPad上看优酷视频时,iPad上的Reminders突然跳出提示框,优酷视频随之暂停. MacB ...

  4. 高手速成android开源项目【导航篇】

    Android开发又将带来新一轮热潮,很多开发者都投入到这个浪潮中去了,创造了许许多多相当优秀的应用.其中也有许许多多的开发者提供了应用开源项目,贡献出他们的智慧和创造力.学习开源代码是掌握技术的一个 ...

  5. 写给自己看的Linux运维基础(一) - 系统基础

    查看内核版本信息 uname -a 查看发行版本 cat /etc/issue 查看硬件配置 CPU: cat /proc/cpuinfo      more /proc/cpuinfo | grep ...

  6. Backbone Collection——数据模型集合

    如果将一个Model对象比喻成数据库中的一条记录,那么Collection就是一张数据表.它表示为一个模型集合类,用于存储和管理一系列相同类型的模型对象. 1.创建集合集合用于组织和管理多个模型,但它 ...

  7. 安装samba服务器

    首先理解以下概念: ftp:在内网和公网使用. 服务器端支持:windows,linux 客户端端支持:windows,linux samba:只能在内网使用,类似于windows的网络邻居(文件共享 ...

  8. Nagios学习笔记一:基本安装和配置

    ()解决安装Nagios的依赖关系: Nagios基本组件的运行依赖于httpd.gcc和gd.可以通过以下命令来检查nagios所依赖的rpm包是否已经完全安装: # yum -y install ...

  9. EndPoint详解

    EndPoint详解 EndPoint主要用于暴露一些SpringMvc内部运行的信息,通常是通过SpringMvc的请求地址获取相关信息.如/health获取健康检查信息. 简单单元测试 @Test ...

  10. if else重复十多次的业务代码也是醉了

    嗯,一个页面同时刷这8个接口,我说能不能合并到一个网络接口,不用一个页面并发8个请求,他说太长了,不好合并. 我看了一下他代码,也是醉了,写了8个接口,访问的都是一个表,然后每个接口重复if else ...