Swift3.0中關(guān)于日期類

處理日期的常見情景

NSDate -> String & String -> NSDate

日期比較

日期計(jì)算(基于參考日期 +/- 一定時(shí)間)

計(jì)算日期間的差異

拆解NSDate對(duì)象(分解成year/month/day/hour/minute/second 等)

NSDate相關(guān)類

NSDate

DateFormatter

DateComponents

DateComponentFormatter

Calendar

Date structure: Swift3.0中引入了Date structure, 和NSDate提供的功能相似, 并且Date結(jié)構(gòu)體和NSDate類可以在Swift中交替使用以此達(dá)到和Objective-C APIs的交互. Date結(jié)構(gòu)體的引入和Swift中引入了String, Array等類型一樣, 都是為了讓對(duì)應(yīng)的類橋接Foundation中對(duì)應(yīng)的類(String -> NSString, Array -> NSArray)

注: 在下面的代碼片段中, NSDate和Date會(huì)交替使用,功能都是相同的.

基本概念

在具體開始寫代碼之前, 搞清楚一些基本的概念是十分必要的:

NSDate對(duì)象: 同時(shí)可以描述日期和時(shí)間, 當(dāng)要處理日期或者時(shí)間時(shí)會(huì)使用到.

DateFormatter對(duì)象: 格式對(duì)象只要在將NSDate和String相互轉(zhuǎn)換的時(shí)候才有價(jià)值, 它是用來規(guī)定格式的. 包括系統(tǒng)自帶的格式和手動(dòng)自定義的格式,同時(shí)該類也支持時(shí)區(qū)的設(shè)置.

DateComponents類: 可以看做是NSDate的姊妹類. 因?yàn)樗峁┝撕芏鄬?shí)用的特性和操作. 最重要的一個(gè)特性就是它可以把日期或者時(shí)間拆解開來, 即日期或者時(shí)間里的每一個(gè)部分(比如年,月,日,小時(shí),分鐘等)都可以單獨(dú)取出來,并且進(jìn)行其他的操作(比如計(jì)算).

DateComponentsFormatter類: 用來將計(jì)算機(jī)讀入的日期和時(shí)間輸出為了人類可讀的字符串.

Calendar類: 日期類可以實(shí)現(xiàn)NSDate和DateComponents之間的轉(zhuǎn)換.

NSDate和String之間的轉(zhuǎn)換

獲得當(dāng)前的日期和時(shí)間

1

2

let currentDate = Date()

print(currentDate) // 2016-08-19 05:33:48 +0000 格林威治時(shí)間

初始化DateFormatter類

1

2

let dateFormatter = DateFormatter()

dateFormatter.locale = Locale.current() // 設(shè)置時(shí)區(qū)

使用系統(tǒng)自帶樣式輸出日期

在將NSDate對(duì)象轉(zhuǎn)換成String類型前, 首先需要告訴計(jì)算機(jī)你想要輸出什么樣的日期格式. 這里有兩種方式. 第一就是使用系統(tǒng)自帶的格式, 第二個(gè)方法是手動(dòng)使用特定的說明符來指定輸出格式.這里先看系統(tǒng)自帶格式的輸出.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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個(gè)E就代表簡(jiǎn)寫,比如Mon.

MMMM: 代表一個(gè)月的全名,比如July.使用1-3個(gè)M就代表簡(jiǎn)寫,比如Jul.

dd: 代表一個(gè)月里的幾號(hào),比如07或者30.

yyyy: 代表4個(gè)數(shù)字表示的年份,比如2016.

HH: 代表2個(gè)數(shù)字表示的小時(shí),比如08或17.

mm: 代表2個(gè)數(shù)字表示的分鐘,比如01或59.

ss: 代表2個(gè)數(shù)字表示的秒,比如2016.

zzz: 代表3個(gè)字母表示的時(shí)區(qū),比如GTM(格林尼治標(biāo)準(zhǔn)時(shí)間,GMT+8為北京所在的時(shí)區(qū),俗稱東八區(qū))

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

系統(tǒng)自帶的樣式不夠用時(shí), 就可以使用自定義說明符自定義Date的輸出格式.

自定義說明符的另一個(gè)巨大的作用就是可以將復(fù)雜的字符類型的日期格式(比如Fri, 08 Aug 2016 09:22:33 GMT)轉(zhuǎn)換成Date類型.

自定義格式的使用最重要的就是自定義說明符的使用,說明符是一些對(duì)日期對(duì)象有特點(diǎn)含義的簡(jiǎn)單的字符.下面首先列舉一些這里會(huì)用到的說明符:

關(guān)于說明符的具體介紹,請(qǐng)參照官方文檔

繼續(xù)來看自定義說明符的實(shí)際使用, 下面將現(xiàn)在的日期轉(zhuǎn)換成字符串類型, 并且輸出星期和月份的全名, 年份和天數(shù)用數(shù)字表示:

1

2

3

dateFormatter.dateFormat = "EEEE, MMMM, dd, yyyy"

stringDate = dateFormatter.string(from: currentDate)

print(stringDate) // Friday, August, 19, 2016

從例子中可以很直觀的看到其實(shí)自定義輸出格式的技術(shù)很簡(jiǎn)單, 具體的輸出格式根據(jù)具體的需求而定, 最后再舉一個(gè)例子(輸出格式--> 小時(shí):分鐘:秒 星期簡(jiǎn)寫 月份顯示數(shù)字 天數(shù)顯示數(shù)字 時(shí)區(qū) 公元):

1

2

3

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轉(zhuǎn)String, 這個(gè)轉(zhuǎn)換過程的逆轉(zhuǎn)換更加有趣. 之前用到的系統(tǒng)自帶的輸出格式和自定義的說明符在String轉(zhuǎn)Date的過程中同樣適用. String轉(zhuǎn)Date過程中最重要的一點(diǎn)就是要設(shè)置合適的格式對(duì)應(yīng)與String, 否則輸出會(huì)是nil.

1

2

3

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

這里需要注意的是字符串的時(shí)間是18:15:59, 而輸出的時(shí)間是10:15:59, 原因是因?yàn)槲宜诘臅r(shí)區(qū)是北京所在的時(shí)區(qū),即東八區(qū),而默認(rèn)輸出是按格林尼治標(biāo)準(zhǔn)時(shí)間輸出的,即相差8小時(shí).

下面舉一個(gè)更復(fù)雜的例子, 包括時(shí)區(qū)的輸出:

1

2

3

4

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

在很多情景中,你可能需要將日期的值進(jìn)行拆解,進(jìn)而提取其中特定的值.比如你可能需要得到一個(gè)日期中天數(shù)和月份的值,或者從時(shí)間里面得到小時(shí)和分鐘的值,這部分將會(huì)介紹DateComponents類以此來解決這個(gè)問題. 需要注意的是,DateComponents類會(huì)經(jīng)常和Calendar搭配使用,具體來講,Calendar類的方法是真正實(shí)現(xiàn)了Date到DateComponents的轉(zhuǎn)換,以及逆轉(zhuǎn)換.記住這一點(diǎn)之后,首先先得到currentCalendar對(duì)象,并且將它賦值給常量以便于后面的使用.

1

let calendar = Calendar.current()

下面的代碼通過一個(gè)典型的例子演示一遍Date -> DateComponents的過程.

1

2

3

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:)方法. 這個(gè)方法接收兩個(gè)參數(shù), 第二個(gè)傳入的參數(shù)是將要被拆解的日期對(duì)象,第一個(gè)參數(shù)比較有意思, 這個(gè)參數(shù)是一個(gè)數(shù)組,里面放入組成日期的各個(gè)成分單位(Calendar.Unit),比如月(Calendar.Unit.month), 日(Calendar.Unit.day).

Calendar.Unit是一個(gè)結(jié)構(gòu)體, 它里面的所有屬性及說明可以在官方文檔中查看.

還需要注意的一點(diǎn)就是在components(_:from:)方法的第一個(gè)數(shù)組參數(shù)中,如果沒有傳入想要解析的單位名稱,之后從DateComponents對(duì)象中是得不到這個(gè)單位的, 比如上面的方法中沒有傳入Calendar.Unit.month, 那么最后打印的時(shí)候如果也打印了該值, 得到的值會(huì)是nil.

1

2

3

dateComponents = calendar.components([Calendar.Unit.year], from: currentDate)

print("year:(dateComponents.year) month:(dateComponents.month)")

// Optional(2016) month:nil

DateComponents -> Date

DateComponents -> Date的轉(zhuǎn)換也十分簡(jiǎn)單, 只需要初始化一個(gè)DateComponents對(duì)象, 然后指定特定的components, 最后調(diào)用Calendar類的dateFromComponents:方法完成轉(zhuǎn)換

1

2

3

4

5

6

7

8

9

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)

這里同樣可以設(shè)置不同的時(shí)區(qū)來得到當(dāng)?shù)氐臅r(shí)間:

1

2

3

components.timeZone = TimeZone(abbreviation: "GMT") // Greenwich Mean Time

components.timeZone = TimeZone(abbreviation: "CST") // China Standard Time

components.timeZone = TimeZone(abbreviation: "CET") // Central European Time

比較日期和時(shí)間

除了以上提到的應(yīng)用場(chǎng)景, 另一個(gè)關(guān)于日期和時(shí)間常見的應(yīng)用場(chǎng)景就是比較. 通過對(duì)兩個(gè)Date對(duì)象的比較以此來判斷哪個(gè)日期更早或者更遲,或者是否日期相同. 這里將會(huì)介紹3種方法來做比較, 方法不分好壞, 適合自己的需求最重要.

在開始進(jìn)行比較之前, 先創(chuàng)建兩個(gè)Date對(duì)象用于下面的比較:

1

2

3

4

5

6

7

8

9

10

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:)

當(dāng)比較date1和date2兩個(gè)日期哪個(gè)更早或更晚時(shí), 使用NSDate類提供的earlierDate:和laterDate:方法就可以實(shí)現(xiàn).

1

2

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

當(dāng)使用earlierDate:方法時(shí), 返回值是日期更早的NSDate對(duì)象; laterDate:的返回值是日期更晚的NSDate對(duì)象.

上面的方法中, 因?yàn)閐ate1和date2屬于Date類,而earlierDate:和laterDate:方法想要接收的參數(shù)類型是NSDate, 所以先進(jìn)行了類型轉(zhuǎn)換.

方法2 (compare: )

第二個(gè)比較的方法是使用Date結(jié)構(gòu)體提供的compare:方法, 返回值是ComparisonResult類型的枚舉.

1

2

3

4

5

6

7

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)

第三個(gè)方法有點(diǎn)不同, 原理是分別將date1 和 date2 與一個(gè)參考日期進(jìn)行比對(duì), 然后通過判斷兩個(gè)日期和參考日期的差距, 進(jìn)而判斷兩個(gè)日期的差距.

舉一個(gè)例子: 比較4和10兩個(gè)數(shù)字, 先選取6作為一個(gè)參考數(shù)字, 4-6=-2;10-6=4,4-(-2)=6.

1

2

3

4

5

6

7

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")

}

日期的計(jì)算

處理日期的另一個(gè)有趣的場(chǎng)景就是日期的計(jì)算. 比如計(jì)算2016-08-12號(hào)加2個(gè)月零12天是多少諸如此類的計(jì)算. 這里將介紹兩種不同的方法來進(jìn)行日期計(jì)算. 第一個(gè)方法使用了Calendar類的CalendarUnit結(jié)構(gòu)體; 第二個(gè)方法使用到了DateComponents類.

首先記住當(dāng)前的日期:

1

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

下面假設(shè)想計(jì)算當(dāng)前日期再加2個(gè)月零5天

1

2

let monthsToAdd = 2

let daysToAdd = 5

方法1

1

2

3

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:方法. 這個(gè)方法就是將特定的日期單位值加到現(xiàn)有的日期值上, 然后返回一個(gè)新的Date對(duì)象. 因?yàn)檫@里的例子要加兩個(gè)值, 所以需要調(diào)用該方法兩次.

方法2

方法1雖然可行, 但有缺點(diǎn), 即每一個(gè)單位的計(jì)算都需要分別調(diào)用方法, 假如想給當(dāng)前日期加上7年3個(gè)月4天8小時(shí)23分鐘34秒, 那么就要調(diào)用dateByAddingUnit:value:toDate:options:方法6次! 而方法2則可以對(duì)這個(gè)問題迎刃而解. 而方法2有一次使用到了DateComponents這個(gè)類.

下面我將首先初始化一個(gè)新的DateComponents對(duì)象, 然后設(shè)置月份和天數(shù), 然后再調(diào)用Calendar類中名為dateByAddingComponents:toDate:options:的方法, 該方法的返回值就是計(jì)算好的Date對(duì)象.

1

2

3

4

5

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)

日期差值的計(jì)算

計(jì)算兩個(gè)日期間具體的差值也是在處理日期對(duì)象時(shí)經(jīng)常會(huì)遇到的問題. 這里將介紹3中方法來解決該問題.

首先, 先自定義兩個(gè)日期對(duì)象:

1

2

3

4

5

6

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類中的一個(gè)對(duì)象方法components:from:to:options:.

1

2

3

4

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:方法中的第一個(gè)參數(shù)還是接收一個(gè)包含了Calendar.Unit結(jié)構(gòu)體的數(shù)組. 因?yàn)槭菑膁ate2比date1,升序的返回值就是正數(shù),如果是反過來比,返回值就是負(fù)數(shù).

方法2

這里介紹的第二個(gè)方法將會(huì)第一次使用到DateComponentsFormatter類. DateComponentsFormatter類提供了很多不同的方法進(jìn)行日期間自動(dòng)的計(jì)算,并且返回值是格式化的字符串.

首先初始化一個(gè)DateComponentsFormatter對(duì)象, 然后先指定它的一個(gè)屬性值.

1

2

let dateComponentsFormatter = DateComponentsFormatter()

dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full

1

2

3

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屬性會(huì)指定最后結(jié)果輸出的格式. 這里使用full,就代表結(jié)果輸出的單位會(huì)全名顯示,如Monday,June等.如果想用簡(jiǎn)寫,就可以重新設(shè)置屬性.官方文檔中列出了所有的屬性值。

方法3

1

2

3

dateComponentsFormatter.allowedUnits = [Calendar.Unit.year]

diffString = dateComponentsFormatter.string(from: date1!, to: date2!)

print(diffString) // Optional("-31 years")

最后一個(gè)方法調(diào)用了stringFrom:to:方法來計(jì)算. 注意使用該方法之前, 必須至少在allowedUnits屬性中設(shè)置一個(gè)calendar unit, 否則這個(gè)方法將會(huì)返回nil, 所以在使用該方法之前, 先指定想要怎么樣看到輸出, 然后再讓執(zhí)行輸出的方法。

總結(jié)

就像我在摘要中說的, 處理日期很常見, 在日常代碼中也不可避免, 這里我通過一些小的代碼片段介紹了處理日期的一些常見方法. 不管是NSDate類,Date結(jié)構(gòu)體還是與之相關(guān)聯(lián)的類, 它們的目的只有一個(gè), 就是能夠快速的處理日期. 如果想深入掌握有關(guān)日期的處理, 還是要在日常編碼過程中多多練習(xí), 多多閱讀官方文檔。

-> http://www.cocoachina.com/swift/20160823/17406.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容