iOS 日期處理 (Swift3.0 NSDate)

来源:http://www.cnblogs.com/xiaopin/archive/2016/09/20/5887287.html
-Advertisement-
Play Games

處理日期的常見情景 NSDate -> String & String -> NSDate 日期比較 日期計算(基於參考日期 +/- 一定時間) 計算日期間的差異 拆解NSDate對象(分解成year/month/day/hour/minute/second 等) NSDate相關類 NSDate ...


處理日期的常見情景

  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中關於日期類的使用指引

 

其他開發過程中遇到的問題,後續將進行補充。。。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先看效果圖: 1.Bundle類的作用 Bundle類用作攜帶數據,它類似於Map,用於存放key-value名值對形式的值。相對於Map,它提供了各種常用類型的putXxx()/getXxx()方法,如:putString()/getString()和putInt()/getInt(),putX ...
  • 思路: 1.new 一個 Empty 尾碼為 .txt 文件,內容隨筆拷貝一段 2.用 NSString 接收本地文件,再用一個標題拼接字元串 3.創建一個 NSMutableParagraphStyle 實例,設置標題居中、標題距離正文間隔 4.創建一個 NSMutableParagraphSty ...
  • 先看效果圖: Android為我們提供了四種應組件,分別為Activity、Service、Broadcast receivers和Content providers,這些組建也就是我們開發一個Android應用程式的基石。系統可以通過不同組建提供的切入點進入到開發的應用程式中。對用戶來說不是所有的 ...
  • 前臺功能劃分 我的 登錄 賬戶+密碼 註冊 訂單管理 查看/刪除(顯示訂單詳情) 支付(提交訂單) 線上支付 貨到付款 選擇配送(配送時間、(配送方式 自提 送貨上門)) 查看積分 積分顯示 地址管理 添加 刪除 修改 購物車 查看所有選中商品 修改選擇商品數量 取消選中商品 分類 所有分類(自定義 ...
  • 從 Xcode 8.0 開始,目前所有的插件都無法工作! NSLog 無法輸出 -- 此bug等待正式版本... Xcode 提供了文檔註釋快捷鍵option + cmd + / 但是要把系統升級到10.11.5、 Interface Builder 界面構建器——加速 編輯器擴展 Editor E ...
  • 介紹:MD版的花瓣網App運行效果: <ignore_js_op> 源碼下載:http://code.662p.com/view/13784.html 使用說明:架構 這個項目在寫在很久之前,當時MVP架構網路上各種分析描述,但是感覺都是各說各的。不確定項目是否採用MVP架構就先動手寫代碼,隨時準備 ...
  • 詳細說明:http://android.662p.com/thread-6599-1-1.html 代碼我已經上到那個安卓教程網了,需要的朋友可以參考一下,該案例僅供參考和學習,不得用於商業使用,謝謝。 ...
  • 先看演示效果: 1 ViewPager類提供了多界面切換的新效果。 新效果有如下特征: [1] 當前顯示一組界面中的其中一個界面。 [2] 當用戶通過左右滑動界面時,當前的屏幕顯示當前界面和下一個界面的一部分。 [3]滑動結束後,界面自動跳轉到當前選擇的界面中 2 介紹裡面幾個比較重要的方法與介面 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...