Swift 的 NSDate 初學者指南

做者:gabriel theodoropoulos,原文連接,原文日期:2015-10-18
譯者:ray16897188;校對:numbbbbb;定稿:Ceehtml

若是問我在作過的全部項目中作的最多的事情,那處理日期絕對是榜上有名(譯註:本文中的「日期」是指代 NSDate 對象,同時包含「日(date)」 和「時(time)」這兩個元素)。毋庸置疑,不管工做量是可能是少,開發者早晚須要「玩」一下 NSDate 類,去按某種方式處理一下日期。從簡單的將一個日期轉換成一個字符串到對日期作計算,總會有一個不變的事實:開發者必須在 iOS 編程中學會這個知識點。這並不難掌握,並且能夠爲之後更重要任務節省時間。在新手看來,對日期的操做很麻煩;然而事實並不是如此。你須要作的就是掌握它。ios

在應用中對日期(NSDate)對象最多見的操做就是把它轉換成一個字符串對象,這樣就能夠用正確的格式把它展現給用戶。反向操做也很常見:把字符串轉換成日期對象。然而日期的操做並不僅有這些。下面是一個簡單的列表,列出了除上述操做以外能夠對日期進行的其餘操做:git

  • 日期之間的比較。程序員

  • 計算將來或者過去的日期,很簡單:用一個參考日期(好比當前日期)加上或者減去一段時間(天、月、年等等)。github

  • 計算不一樣日期之間的差值(好比算出兩個特定日期之間的時間間隔有多久)。編程

  • 將一個日期按其組成元素(components)作分解,並對每一個部分作分別訪問(天、月等等)。swift

上面列出的全部內容,包括日期和字符串之間的相互轉換,都是這篇教程要討論的主題。在接下來的各個小節中,你會發現只要你知道該用什麼工具以及如何使用它們,你就能爲所欲爲的對日期進行操做。數組

下面的連接清單裏有不少重要的文章,供參考。若是須要深刻了解某些特定知識點,別忘了點擊訪問一下:安全

關於 Demo App

嗯,這個教程咱們不使用 demo 應用(是的,你沒看錯)。取而代之,咱們此次用一個 Playground 來展現你將要看到的全部例子。我是特地這麼作的,由於個人目的是給你提供豐富的、能更好的展現出關於 NSDate 方方面面的代碼。

你能夠下載並在 Xcode 中打開這個寫好的 playground 文件,但我仍是強烈建議你新建一個 Playground 文件,並測試下面章節中的每個新代碼段。這樣會讓你更容易的去理解每一個示例是如何工做的,除此以外你還能夠修改代碼,實時觀察你的修改會如何影響生成的結果。

我給你的 playground 文件名是 PlayingWithDates,裏面包含了全部的代碼。你本身的文件能夠用相同的文件名,或者換一個,都無所謂。

基本概念

在咱們開始查看日期相關的技術細節並思考能用它們作什麼以前,先要確保每一個人都已經掌握一些基本概念,這很重要。先從一個最簡單的開始:NSDate 對象。從程序角度來講這種對象包含了對日(date)時(time)二者的描述,因此它不只僅能夠幫咱們處理「日」,還能夠幫咱們處理「時」。對於 NSDate 對象自己來講是沒有格式(formatting)這個概念的;和其餘類中的全部屬性同樣,能夠把日和時看作是屬性(properties)。只有在將一個日期對象轉換成一個字符串時,格式這個概念纔會派上用場,下面的內容裏咱們會看到不少關於這個的細節。一般來說,記住你所須要的就是 NSDate 這個類,不管你只關心「日」、「時」或者二者。

接下來咱們會遇到的另外一個類是 NSDateComponents。能夠把這個類看作 NSDate 的「姊妹」類,由於這個類給開發者提供了一些極爲有用的特性和操做。這個類的第一個要點是它能夠將「日」部分或者「時」部分做爲一個單獨的屬性顯示出來,因此咱們能夠直接訪問「日」或者「時」,而後在其餘的任務中使用(好比對「日」或「時」的計算)。例如,一個 NSDateComponents 實例中的天和月在下面的代碼中表示爲 daymonth 屬性:

let dateComponents = NSDateComponents()
let day = dateComponents.day
let month = dateComponents.month

就這麼簡單。固然訪問日期元素並將該日期的值傳遞給一個 NSDateComponents 對象須要先作一些強制轉換,這些咱們以後再討論。

除上所述以外,NSDateComponents 這個類在用於計算將來或者過去的日期時也很是有用。當你想獲得一個在某個特定日期以後或以前的那個日期時,你要作的就是加上或者減去合適的那一部分,最終就能轉換成一個新的日期。另外 NSDateComponents 也適合計算日期之間的差值。如今無需深刻研究這兩個內容,咱們一下子會看到細節。

對於 NSCalendar 類,雖然它不會派上大用場,並且咱們僅須要用它來實現 NSDateNSDateComponents 相互轉換,但它在咱們的日期遊戲中也是重要的一員。關於它所支持的特性,本文不會再進行介紹。將日期從 NSDate 轉換成 NSDateComponents(或者反過來)的任務屬於 NSCalendar 類,按照慣例,作轉換須要一個特定的 c alendar(日曆)對象。實際上系統在作任何轉換以前都須要知道要用一個怎樣的 calendar 對象,從而纔可能給出正確的結果(別忘了滿世界有太多不一樣的 calendar 對象,轉換出來的「天」、「月」等值會千差萬別)。你能夠讀一些和 calendar 有關的文章(參考簡介裏的連接),而在這裏爲圖簡便,咱們會用 NSCalendar 的類方法 currentCalendar() 來獲得用戶設置中指定的 calendar。

此外,在下一節中咱們會使用一個特別好的工具,它就是 NSDateFormatter 類。它可以實現 NSDate 對象到字符串、以及字符串到 NSDate 對象的轉換。它還可使用預約義的日期樣式(date styles)來給最終的日期字符串制定格式,或是經過給出指望格式的描述來實現高度格式樣式定製。下面會有一些相關的例子,其中一些例子示範了雙向轉換。一個 NSDateFormatter 對象一樣也支持本地化(localization);咱們所須要的就是給它提供一個有效的 NSLocale 對象,基於該給定的位置(locale)設置最終轉換出的對象就會正確顯示出來。 

還有個相似的 NSDateComponentsFormatter 類,它能夠將「日」和「時」部分做爲輸入,輸出人類可讀的、有特定格式的日期字符串。對此這個類包含了不少方法(methods),在此教程的最後一部分咱們會看見其中幾個;咱們只討論在教程的例子中用到的那些知識點。

上面已經說了那麼多,咱們能夠開始編程了,具體學習上面提到的每一個類的用法。再說一次,建議你建立一個新的 playground 文件,而後把我介紹的每一條都試一下。沒有什麼學習方法比親手作更有效果。

NSDate 和 String 之間的轉換

首先,咱們使用 NSDate 得到當前日期,並將它賦給一個常量以便訪問。和其餘一些語言所要求的不一樣,得到當前的日期並不須要調用相似 now() 或者 today() 的特殊方法。你所要作的就是初始化一個 NSDate 對象:

let currentDate = NSDate()

在 Xcode 的 playground 裏敲入上面的語句,你會看到:

注意咱們會在下面的代碼中屢次使用到上面的這個值。如今初始化一個 NSDateFormatter 對象。它用來在 dates 和 strings 之間作轉換。以下:

let dateFormatter = NSDateFormatter()

除非是有其餘明確的設定,不然 dateFormatter 會默認採用設備中的位置(locale)設置。儘管系統並不要求你去手動設置當前的位置,但若是須要的話你能夠這麼作:

dateFormatter.locale = NSLocale.currentLocale()

設一個不一樣的位置很容易:你僅須要知道與位置(locale)相匹配的位置標識符(locale identifier)是什麼,而後指定給 locale 屬性便可:

dateFormatter.locale = NSLocale(localeIdentifier: "el_GR")
dateFormatter.locale = NSLocale(localeIdentifier: "fr_FR")

這兩行代碼展現瞭如何給 date formatter 去設置一個不一樣的位置(例子裏分別是希臘和法國地區)。固然設置多個位置的值沒有意義,由於能起做用的僅僅是最後一個。你是否是想知道 locale 是怎麼影響日期和字符串之間的轉換的呢?過會兒你就會獲得答案。

用 Date formatter styles 爲輸出結果設置格式

把一個日期對象(NSDate)轉換成一個字符串以前,你須要「告訴」date formatter 你要獲得的字符串結果的格式是怎樣的。這裏有兩種方法。第一種是使用預約義的 date formatter styles,第二種是使用某些特定的分類符(specifier)來手動指定最終輸出結果的格式。

先用第一種方法,咱們須要使用 NSDateFormatterStyle enum。這個枚舉類型的每個枚舉值都表明一種不一樣的格式樣式類型。第一個樣式是 FullStyle,下面的圖片是使用它的效果:

下面是上面代碼的文本,想複製的話隨意:

dateFormatter.dateStyle = NSDateFormatterStyle.FullStyle
var convertedDate = dateFormatter.stringFromDate(currentDate)

除了日期樣式(date style)以外,上面兩行代碼中的 stringFromDate: 方法也同等重要,這個方法實現了真正的轉換。當談及轉換時,咱們實際上說的是這個方法,其他的只不過是自定義結果格式過程當中所需的一些步驟。若是你想要在你的項目裏作日期的轉換,那麼這個方法對你來講確定很是方便。

好,來看下一個樣式,Long Style

文本形式的代碼:

dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle
convertedDate = dateFormatter.stringFromDate(currentDate)

能夠看到這種類型的樣式中不包含星期幾(和 Full Style 相比而言)。下面是 Medium Style

dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
convertedDate = dateFormatter.stringFromDate(currentDate)

最後是 Short Style

dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
convertedDate = dateFormatter.stringFromDate(currentDate)

如今你已經知道可用的 date formatter styles 都是什麼了,你能夠根據項目需求去使用它們。每種樣式的設置都會產生出一個不一樣的結果,可能其中有一種會適合你。

以前我說過 date formatter 的 locale 能夠被設置成非默認值。如今咱們已經看到如何使用 date formatter styles 作轉換,咱們再來看看不一樣的 locale 值如何改變初始日期的字符串轉換結果。下面的例子中我會使用 Full Style,以及前面提到的兩個 locale identifier(希臘和法國)。

我想如今 locale 能作什麼你已經很清楚了,好好使用它吧。

使用 Date format specifier

上面的 date formatter style 足以應對多數狀況,可是咱們沒法經過修改這些格式來得到不一樣於預設格式的結果。這種狀況下咱們還有另外一個選擇,一個能設置自定義 date format 的能力,這個自定義的 date format 可以正確描述你理想中的 date formatter 對象的的格式樣式。通常來講設置一個自定義的 date format 對如下兩種狀況很適用:當 date formatter style 實現不了你所指望的輸出結果的樣式時(顯然),還有當你須要把一個複雜的日期字符串(好比「Thu, 08 Oct 2015 09:22:33 GMT」)轉換成一個日期對象時。

爲了正確的設置一個 date format,必定要用一組分類符(specifier) 。Specifier 不過是一些簡單的字符,這些字符對 date formatter 對象來講有着特定的意義。在我給你具體的例子以前,先列出來一些在接下來的代碼中會使用到的format specifier:

  • EEEE:表示星期幾(如 Monday)。使用 1-3 個字母表示周幾的縮略寫法。

  • MMMM:月份的全寫(如 October)。使用 1-3 個字母表示月份的縮略寫法。

  • dd:表示一個月裏面日期的數字(如 09 或 15)。

  • yyyy:4 個數字表示的年(如 2015)。

  • HH:2 個數字表示的小時(如 08 或 19)。

  • mm:2 個數字表示的分鐘(如 05 或者 54)。

  • ss:2 個數字表示的秒。

  • zzz:3 個字母表示的時區(如 GMT)。

  • GGG:BC 或者 AD。

若是想查看 date format specifiers 的參考內容,建議訪問官方技術規範,你能夠找到上面給出的 specifier 的使用方法,以及那些沒有列出的 specifier。

繼續咱們的例子,看一下 format specifier 具體怎麼用。這回咱們把當前日期轉換成一個字符串,顯示成具備星期名稱、月的全寫,日期數字和年份的格式:

dateFormatter.dateFormat = "EEEE, MMMM dd, yyyy"
convertedDate = dateFormatter.stringFromDate(currentDate)

我想怎麼用自定義的 date format 已經不須要額外的講解了,用法十分簡單。再來一個例子,轉換一下時間:

dateFormatter.dateFormat = "HH:mm:ss"
convertedDate = dateFormatter.stringFromDate(currentDate)

到如今爲止咱們看到的全部轉換都是從 NSDate 對象變成一個有特定格式的字符串。相反的操做也頗有意思,以前關於 date formatter styles 和 format specifiers 的也一樣適用。把有既定格式的字符串轉換成一個 NSDate 對象的關鍵是要對 date formatter 的 dateFormat 屬性作出正確設置,而後調用 dateFromString: 方法。咱們再看幾個例子:

var dateAsString = "24-12-2015 23:59"
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm"
var newDate = dateFormatter.dateFromString(dateAsString)

再看一個更復雜的字符串,還包含了時區:

dateAsString = "Thu, 08 Oct 2015 09:22:33 GMT"
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
newDate = dateFormatter.dateFromString(dateAsString)

注意一下時間(09:22)是如何經過簡單的、在日期字符串中引入了一個時區而發生改變的(變成了 12:22)。這裏沒有任何實際上的變化,僅僅是我所在的時區(EFT)的時間在 GMT 時區中的表示,基於上面的代碼,根據你本身的狀況自由發揮吧。

到這裏你已經基本上看到了爲實現日期和字符串之間的轉換你所須要的全部知識點。你能夠敲敲本身的代碼,試試你在上面所看到的那些,深刻感覺一下這些東西是如何工做的。

使用 NSDateComponents

不少時候你須要在項目裏拆分一個日期對象,而後從中得到特定組成元素的值。例如你可能會從一個日期對象中獲取它的日和月的值,或者從時間中得到小時和分鐘的值。此種狀況下你須要用到的工具就是 NSDateComponents 這個類。

NSDateComponents 類一般和 NSCalendar 類相結合來使用。具體點說,NSCalendar 方法實現了真正的從 NSDateNSDateComponents 對象的轉換;以及咱們待會兒會看到的,從日期的組成元素到日期對象的轉換。記好了這一點,這一節中咱們首先要作的就是獲取當前的 calendar,把它賦給一個常量以便訪問:

let calendar = NSCalendar.currentCalendar()

如今咱們看一個典型例子,一個 NSDate 對象是怎樣被轉換成一個 NSDateComponents 對象,以後我會作些講解:

let dateComponents = calendar.components([NSCalendarUnit.Day, NSCalendarUnit.Month, NSCalendarUnit.Year, NSCalendarUnit.WeekOfYear, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second, NSCalendarUnit.Nanosecond], fromDate: currentDate)

print("day = \(dateComponents.day)", "month = \(dateComponents.month)", "year = \(dateComponents.year)", "week of year = \(dateComponents.weekOfYear)", "hour = \(dateComponents.hour)", "minute = \(dateComponents.minute)", "second = \(dateComponents.second)", "nanosecond = \(dateComponents.nanosecond)" , separator: ", ", terminator: "")

上面第一行代碼用的方法是 NSCalendar 類的 components(_:fromDate:) 。該方法接受兩個參數:第二個參數是原日期對象,咱們要從中得到它的組成元素。但有意思是第一個參數,該方法要求第一個參數是一個元素爲 NSCalendarUnit 屬性的數組,這些屬性對要從日期對象中抽取出的元素作出了描述。

NSCalendarUnit 是一個結構體,你能夠從這裏看到全部可用的屬性。上面的例子中,在你看到的代碼段截圖中給定的這些 calendar unit 值返回以下構成部分:

  • Day

  • Month

  • Year

  • Week of year

  • Hour

  • Minute

  • Second

  • Nanosecond

注意到在第一個參數數組中那些沒有列出的 calendar unit(日曆單元)在調用方法以後是不可用的。例如因爲咱們沒有將 NSCalendarUnit.TimeZone 這個單元包括進去,因此在剩下獲取到的元素中是訪問不到時區(timezone)的(好比用 print(dateComponents.timezone))。這麼作的話會獲得一個運行時錯誤。若是你須要額外的部分,你就必須再調用一次該方法,指定你想要的額外的calendar units。

從 date components 轉換到日期對象也很容易。這回不會涉及到對 calendar unit 的使用。所須要的就是初始化一個新的NSDateComponents對象,而後明確指定出全部須要的components元素(固然是根據你app的須要),而後調用 NSCalendar 類的 dateFromComponents 方法實現轉換。來看一下:

let components = NSDateComponents()
components.day = 5
components.month = 01
components.year = 2016
components.hour = 19
components.minute = 30
newDate = calendar.dateFromComponents(components)

前面的部分咱們看過一個在把某特定格式的字符串轉換成一個日期對象時使用了 timezone 的例子。若是你足夠好奇想看看對一個日期對象設置不一樣 timezone 的結果,咱們就將上面的代碼稍稍擴展一下,看看 timezone 的多種取值:

components.timeZone = NSTimeZone(abbreviation: "GMT")
newDate = calendar.dateFromComponents(components)
 
components.timeZone = NSTimeZone(abbreviation: "CST")
newDate = calendar.dateFromComponents(components)
 
components.timeZone = NSTimeZone(abbreviation: "CET")
newDate = calendar.dateFromComponents(components)

GMT = 格林威治標準時間
CST = 中國標準時間
CET = 歐洲中部時間

你能夠在這裏找到全部 timezone 的縮寫,還有一些很棒的在線工具。

如今你也知道如何去處理 NSDateComponents 對象了,那麼我們繼續來研究另外一個有意思的東西。

比較日期和時間

處理日期的另一個常見狀況是須要對兩個日期對象進行比較,判斷哪個表明着更早或者更晚,甚至比較這兩個是否爲同一日期。歸納來講我在下面會告訴你三種不一樣的比較日期對象的方式,但我不但願讓你有種哪一個是最好或者最壞的觀點。很明顯這取決於你在你的應用中想要幹什麼,而每種方式和其餘兩種都有些不一樣,哪一種方法對你幫助最有效就選哪一種。

在比較日期對象的方法給出以前,咱們先建立兩個日期對象,在本節的例子中使用。首先設定日期格式(date formatter 的 dateFormat 屬性),而後把兩個日期格式的字符串轉換成兩個日期對象:

dateFormatter.dateFormat = "MMM dd, yyyy zzz"
dateAsString = "Oct 08, 2015 GMT"
var date1 = dateFormatter.dateFromString(dateAsString)!
 
dateAsString = "Oct 10, 2015 GMT"
var date2 = dateFormatter.dateFromString(dateAsString)!

先看看用來比較日期的第一個方式。若是你想要比較兩個日期中比較早的那一個,那麼 NSDate 類會給你提供較大幫助,它分別提供了兩個方法,earlierDate:laterDate:。這兩個方法的語法很簡單:

date1.earlierDate(date2)

原理以下:

  • 若是 date1 對象比 date2 更早,那麼上面的方法會返回 date1 的值。

  • 若是 date2 對象比 date1 更早,那麼上面的方法會返回 date2 的值。

  • 若是二者相等,則返回 date1

一樣道理也使用於 laterDate: 方法。

如今來看咱們的例子,使用咱們以前建立的那兩個日期對象。下面的兩條指令分別使用了剛纔提到的那兩個方法,爲咱們顯示出更早的和更晚的日期:

// Comparing dates - Method #1
print("Earlier date is: \(date1.earlierDate(date2))")
print("Later date is: \(date1.laterDate(date2))")

第二種比較兩個 NSDate 對象的方式使用的是 NSDate 類的 compare: 方法,以及 NSComparisonResult 枚舉類型。看下面的例子就會明白個人意思,可是我先提一下這種方式的語法和我上面例子中的很像。比較日期所得的結果是和全部的可能值做比較,用這種方式能夠很容易的判斷出兩個日期是否相等、哪個更早或者更晚。不說了,下面的代碼已經足夠明瞭:

Playground 中的結果以下:

可複製的代碼:

// Comparing dates - Method #2
if date1.compare(date2) == NSComparisonResult.OrderedDescending {
    print("Date1 is Later than Date2")
}
else if date1.compare(date2) == NSComparisonResult.OrderedAscending {
    print("Date1 is Earlier than Date2")
}
else if date1.compare(date2) == NSComparisonResult.OrderedSame {
    print("Same dates")
}

比較兩個日期對象的第三種方式多少有些不一樣,由於這種方式引入了對 time intervals 的使用。實際上這種方式很簡單,它作的就是得到自每一個日期以來的時間間隔(每一個日期和如今的時間間隔),而後作比較:

// Comparing dates - Method #3
if date1.timeIntervalSinceReferenceDate > date2.timeIntervalSinceReferenceDate {
    print("Date1 is Later than Date2")
}
else if date1.timeIntervalSinceReferenceDate <  date2.timeIntervalSinceReferenceDate {
    print("Date1 is Earlier than Date2")
}
else {
    print("Same dates")
}

上面的代碼也能夠應用到對時間的比較。下面我給你最後一個例子,而此次 date1date2 對象包含了對時間的表示。我再次使用 earlierDate: 方法,但另外還有一個,idEqualToDate:,很明顯,看名字就知道它是幹什麼的:

// Comparing time.
dateFormatter.dateFormat = "HH:mm:ss zzz"
dateAsString = "14:28:16 GMT"
date1 = dateFormatter.dateFromString(dateAsString)!
 
dateAsString = "19:53:12 GMT"
date2 = dateFormatter.dateFromString(dateAsString)!
 
if date1.earlierDate(date2) == date1 {
    if date1.isEqualToDate(date2) {
        print("Same time")
    }
    else {
        print("\(date1) is earlier than \(date2)")
    }
}
else {
    print("\(date2) is earlier than \(date1)")
}

若是看到上面代碼中「2000-01-01」這個日期以後你感受好奇或疑惑的話,不用擔憂。NSDate 若是在沒有給定任何特定日期來作轉換的狀況下會默認將其添加,它不會影響到這個日期中其餘的元素(例子中其餘的元素是時間)。

好了,到這裏你也會怎麼對日期作比較了。

計算出將來或過去的日期

處理日期另外一個有趣的方面就是計算出一個未來或者過去的日期。咱們以前看到的那些用法在這裏會變得很方便,好比 NSCalendarUnit 結構體,或者 NSDateComponents 類。實際上,我會給你展現兩種不一樣的計算出其餘日期的方式,第一種使用的就是 NSCalendar 類和 NSCalendarUnit 結構體,第二種使用的是 NSDateComponents 類。最後我會給出第三種方式,可是通常狀況我不推薦使用(到那部分我會解釋爲何)。

一開始咱們先記一下當前日期(是我寫這篇教程的日期),它會被用做咱們的參考日期:

如今假設咱們想把當前日期加上兩個月零五天,實際上仍是寫下來比較好:

let monthsToAdd = 2
let daysToAdd = 5

咱們如今就能夠看一下第一種方式了,來獲得想要的新日期吧。先給代碼,立刻解釋:

var calculatedDate = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Month, value: monthsToAdd, toDate: currentDate, options: NSCalendarOptions.init(rawValue: 0))
calculatedDate = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Day, value: daysToAdd, toDate: calculatedDate!, options: NSCalendarOptions.init(rawValue: 0))

如你所見,這裏用到的方法是 NSCalendar 類的 dateByAddingUnit:value:toDate:options: 方法。這個方法的任務就是給一個現有的日期加上一個特定的 calendar unit(由第一個參數指定),並將這個加法的結果作爲一個新的日期返回。咱們開始想的是在當前日期的基礎上同時加兩個不一樣的 calendar unit,但很顯然這不現實。因此這裏問題的關鍵是就要連續的調用該方法,每次設置其中的一個 calendar unit,從而獲得最終結果。

下面是每次疊加以後 playground 顯示的結果:

上面的方式不錯,可是僅限於你要加的只有 1~2 個 calendar units,不然你得連續屢次調用上面那個方法才行。

當須要疊加更多的 units 時,第二個,也是更傾向的方式是使用 NSDateComponents 這個類。爲了演示,咱們不會再引入其餘的組成元素,除上面已經定好的月和日以外。在這兒要作的事情很簡單:首先初始化一個新的 NSDateComponents 對象,並給它設置以前定好的月和日。而後調用 NSCalendar 類的另外一個叫作 dateByAddingComponents:toDate:options: 的方法,咱們會當即獲得一個新的 NSDate 對象,這個新對象即表明了最終想要的日期。

let newDateComponents = NSDateComponents()
newDateComponents.month = monthsToAdd
newDateComponents.day = daysToAdd

calculatedDate = NSCalendar.currentCalendar().dateByAddingComponents(newDateComponents, toDate: currentDate, options: NSCalendarOptions.init(rawValue: 0))

注意到上面的兩個代碼段中,我都沒給這兩個新介紹方法的最後一個參數作任何設置。而若是你想對這個可設選項瞭解更多的話,就去參考 NSCalendar 類的官方文檔

第三種計算另外一個日期方式不推薦對時間跨度大的狀況使用,由於因爲閏秒,閏年,夏令時等等會致使這種方式產生出錯誤結果。該方式的想法是給當前日期加上一個特定的時間間隔。咱們會使用 NSDate 類的 dateByAddingTimeInterval: 方法來實現這個目的。下面的例子中咱們算出來一個至關因而 1.5 小時的時間間隔,而後把它加到當前日期上:

let hoursToAddInSeconds: NSTimeInterval = 90 * 60
calculatedDate = currentDate.dateByAddingTimeInterval(hoursToAddInSeconds)

再強調一下,要作任何類型的日期計算的話,仍是使用前兩種方式更安全。但這仍是取決於你,選擇你更喜歡的那一種。

上面的三個例子都是給當前日期加上某些個組成元素。那如今用一樣方式給當前日期減去幾天,算出來那個過去的日期該怎麼作?

下面的代碼示範了該怎麼作。首先給當前日期加上一個特定天數的值,這就能夠獲得一個屬於過去的日期了。而後把結果轉換成一個有適當格式的字符串,最後的結果...頗有意思:

let numberOfDays = -5684718
calculatedDate = NSCalendar.currentCalendar().dateByAddingUnit(NSCalendarUnit.Day, value: numberOfDays, toDate: currentDate, options: NSCalendarOptions.init(rawValue: 0))
 
dateFormatter.dateFormat = "EEEE, MMM dd, yyyy GGG"
dateAsString = dateFormatter.stringFromDate(calculatedDate!)

以上全部的小代碼段示例能夠徹底給你講明白怎樣經過給某個參考日期加上或正或負的 calendar unit 來算出一個新的日期。本身隨便擴展一下上面的代碼吧,寫下你本身的代碼,你就會對這些技巧更加熟悉。

計算出日期的差值

和標題的意思同樣,這節講的是計算出兩個日期之間的差值,它是在你編程生涯中某個時間確定要作的一個任務,顯然須要作不止一次。在這(教程的最後)一部分,我會告訴你計算出兩個 NSDate 對象之間差值的三種方式,你能夠根據須要選出最適合你的那一種。

開始以前先定義兩個 NSDate 對象:

dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateAsString = "2015-10-08 14:25:37"
date1 = dateFormatter.dateFromString(dateAsString)!
 
dateAsString = "2018-03-05 08:14:19"
date2 = dateFormatter.dateFromString(dateAsString)!

有了上面的日期對象,咱們再來看一下如何獲取日期組成元素(date components)形式的日期差值(date difference )。咱們會再次用到 NSCalendar 類,還有它的一個以前咱們沒見過的方法。最後把日期組成元素打印出來看一下結果。很明顯當有了它,這個表明了日期差值的元素以後,想怎麼作都取決於你了。來看下示範:

var diffDateComponents = NSCalendar.currentCalendar().components([NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second], fromDate: date1, toDate: date2, options: NSCalendarOptions.init(rawValue: 0))
 
print("The difference between dates is: \(diffDateComponents.year) years, \(diffDateComponents.month) months, \(diffDateComponents.day) days, \(diffDateComponents.hour) hours, \(diffDateComponents.minute) minutes, \(diffDateComponents.second) seconds")

這種新方式就是使用 components:fromDate:toDate:options: 方法,一樣它的第一個參數是一個 NSCalendarUnit 的數組。注意下若是第一個日期比第二個晚的話,返回值就會是負數。

在計算日期差值的另外兩種方式中,咱們會第一次用到 NSDateComponentsFormatter 類,這個類有不少方法,能自動作出差值計算,而後返回一個帶有特定格式的字符串。先生成一個對象,並先指定它的一個屬性:

let dateComponentsFormatter = NSDateComponentsFormatter()
dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyle.Full

unitsStyle 屬性告訴 dateComponentsFormatter 描述日期差值的那個字符串的格式應該是什麼樣的,顯示出的日期組成元素應該是怎樣的樣式。好比用 Full 這個樣式,星期幾、月份等等就會顯示成常規的全寫(full-length)單詞。而若是咱們用了 Abbreviated 樣式的話,則會顯示這些信息的縮寫。從這裏你能看到關於單元樣式(units style)的全說明列表。

回到日期差值中來,此次咱們要先算出兩個日期之間的時間間隔。而後這個間隔自己會做爲一個參數傳遞給 NSDateComponentFormatter 類的 stringFromTimeInterval: 方法,結果就會以一個格式化好了的字符串形式返回。

let interval = date2.timeIntervalSinceDate(date1)
dateComponentsFormatter.stringFromTimeInterval(interval)

最後,在計算日期差值的最後一個方式中,兩個日期須要做爲參數傳遞給 NSDateComponentsFormatter 類的 stringFromDate:toDate: 方法。然而用這個方法以前須要先知足一個條件:allowedUnits 屬性必需要設置一個 calendar unit,不然該方法會返回一個 nil。因此咱們就「告訴」這個方法咱們想要怎樣的 unit,以後就等它給咱們差值結果:

dateComponentsFormatter.allowedUnits = [NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second]
let autoFormattedDifference = dateComponentsFormatter.stringFromDate(date1, toDate: date2)

總結

簡介部分中我說過,處理 NSDate 對象這件事在你項目中很是常見,並且確定沒法避免。不能否認它並非程序員最喜歡討論的話題,因此我就寫了前面的那些,在小例子中告訴你其實處理日期是很容易的。這篇教程中 NSDate 的方方面面,以及其餘相關的類都有一個共同目標:教你小巧高效的用法,兩三行代碼就讓你把活兒搞定。但願這篇文章能給你作個指南,尤爲若是你是一個新開發者。下篇文章出來以前,好好練習吧。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索