NSCalendar Additions

翻譯自# NSCalendar Additions
日期,一個很普通的時間和它的實現(xiàn)間往往有著巨大的差異净响,里面隱藏的多方面的復(fù)雜性妻味。其中包括亞秒級的精度,重疊單元瓷式,不同的地理位置的時區(qū)邊界替饿,語言和語法上的本地化差異,以及為了夏令時的轉(zhuǎn)換和閏年調(diào)整贸典,而在標(biāo)準(zhǔn)時間中添加刪除整塊的時間等等视卢,里面有太多的東西需要處理。

再開始進(jìn)行任何日期計算相關(guān)的任務(wù)前廊驼,我們有必要深入了解一下我們手中已有的工具据过。相比寫上千個版本的dateIsTomorrow,我覺得更好的辦法是使用Foundation方法。你有在用NSdateComponents嗎妒挎?你有指定正確的日歷單元嗎绳锅?你的代碼在2100年2月28號還能正常工作嗎?

但事實上:大家一直都在使用那些已經(jīng)非常熟悉了的APIs酝掩,很少有人注意到鳞芙,NSCalendar 已經(jīng)添加了一系列功能十分強(qiáng)大的方法去操作計算日期

let calendar = NSCalendar.currentCalendar()

從全新的日期組件存取與日期比較方法,到強(qiáng)大的日期插值與枚舉方法期虾,有太多的東西被我們忽視了原朝。接下來讓我們抽點時間來了解一下吧。

便利的日期組件存取

哇, NSDateComponents 真是既實用又靈活镶苞,但當(dāng)我只是想知道間隔的小時數(shù)時竿拆,它用起來感覺又太麻煩了。不要慌宾尚, NSCalendar 來救你了丙笋!

let hour = calendar.component(.CalendarUnitHour, fromDate: NSDate())

這樣就好多了。NSCalendar煌贴,你還有哪些本事?

  • getEra(_:year:month:day:fromDate:):根據(jù)傳入的日期引用返回紀(jì)元御板,年,月牛郑,日怠肋。不需要的參數(shù)可以傳入 nil/NULL。
    -getEra(_:yearForWeekOfYear:weekOfYear:weekday:fromDate:): 根據(jù)傳入的日期引用返回紀(jì)元淹朋,年笙各,當(dāng)年第幾周钉答,星期幾。不需要的參數(shù)可以傳入 nil/NULL杈抢。
  • getHour(_:minute:second:nanosecond:fromDate:): 根據(jù)傳入的日期引用返回時間信息数尿,然后 nil/NULL 巴拉巴拉, 你懂的。

NSDateComponents惶楼,剛才我是逗你玩呢右蹦,我收回前面吐槽你的話。下面還有不少屬于你的方法:

  • componentsInTimeZone(_:fromDate:): 根據(jù)傳入的的日期和時區(qū)返回一個 NSDateComponents 實例歼捐。
  • components(_:fromDateComponents:toDateComponents:options:): 返回兩個 NSDateComponents 實例間的差異何陆。如果有未賦值的組件,該方法會使用默認(rèn)值豹储,所以我們傳入的實例至少得設(shè)置了年屬性贷盲。options參數(shù)暫時沒有用,傳 nil/0 就行剥扣。

日期比較

雖然直接比較 NSDate 是件挺簡單的事巩剖,但一些更有意義的比較可能變得驚人的復(fù)雜。兩個 NSDate 實例是同一天朦乏?同一小時?亦或是同一周氧骤?

現(xiàn)在沒必要發(fā)愁了呻疹,NSCalendar 提供了大量的比較方法:

isDateInToday(_:): 如果傳入的日期是當(dāng)天返回 true 。
isDateInTomorrow(_:): 如果傳入的日期是明天返回 true 筹陵。
isDateInYesterday(_:): 如果傳入的日期是昨天返回 true 刽锤。
isDateInWeekend(_:): 如果傳入的日期是周末返回 true 。
isDate(_:inSameDayAsDate:): 如果兩個 NSDate 實例在同一天返回 true - 沒必要再去獲取日期部件進(jìn)行比較了朦佩。
isDate(_:equalToDate:toUnitGranularity:): 如果傳入的日期在同一指定單位內(nèi)返回 true 并思。這意味著,兩個在同一周的日期實例調(diào)用calendar.isDate(tuesday, equalToDate: thursday, toUnitGranularity: .CalendarUnitWeekOfYear) 方法時會返回 true 语稠,就算他們不在同一個月也是如此宋彼。
compareDate(_:toDate:toUnitGranularity:): 返回一個 NSComparisonResult,當(dāng)做和任何指定區(qū)間內(nèi)的日期相等仙畦。date(_:matchesComponents:)`: 如果日期匹配指定的部件返回 true 输涕。

日期插值

接下來講一些根據(jù)起始點尋找下一日期的方法。你可以基于一個NSDateComponents實例慨畸,一個指定的日期組建莱坎,或者特定的時分秒,去找到下一個(或上一個)日期寸士。所有這些方法都需要一個NSCalendarOptions參數(shù)去提供更加精細(xì)的控制檐什,特別是一開始我們沒能找到準(zhǔn)確的匹配的時候碴卧,痛可以幫我們確定如何選定下一個日期。

NSCalendarOptions

對于所有的方法NSCalendarOptions來說.SearchBackwards乃正,最簡單的選擇是顛倒每個搜索的方向住册。向后搜索被構(gòu)造為返回類似于向前搜索的日期。例如烫葬,向后搜索hour11 的前一個日期會給你11點界弧,而不是11點59分,即使11:59在技術(shù)上會在“11點之前”向后搜索搭综。事實上垢箕,向后搜索是直觀的,直到你思考它兑巾,然后直到你想得更多条获,才會是直覺的。這.SearchBackwards是容易的部分應(yīng)該給你一些前瞻的想法蒋歌。

接下來的 NSCalendarOptions 選項能夠幫助我們處理那些 “消失” 的時間帅掘。舉個最直觀的例子來說,當(dāng)你進(jìn)行一個短時窗搜索時碰到夏令時調(diào)整堂油,時間提前了一個小時修档。或者搜索時遇到類似 2 月 或者 4 月 31 號府框,它都能幫我們跳過這些缺失的時間吱窝。

當(dāng)遇到缺失的時間時,如果我們設(shè)置了 NSCalendarOptions.MatchStrictly迫靖,相關(guān)方法會根據(jù)傳入的組件尋找一個 精確 的匹配院峡。如果沒有設(shè)置的話,那么必須提供 .MatchNextTime, .MatchNextTimePreservingSmallerUnits, 和 .MatchPreviousTimePreservingSmallerUnits 中的任一項系宜。這些選項決定了如何處理我們請求時遇到的時間缺失問題照激。

這種情況,往往一例勝千言:

// 2015 年情人節(jié)盹牧,早上 9 點
let valentines = cal.dateWithEra(1, year: 2015, month: 2, day: 14, hour: 9, minute: 0, second: 0, nanosecond: 0)!

// 為了找到月的最后一天俩垃, 我設(shè)置一個日期組件然后把 `day` 設(shè)成 31:
let components = NSDateComponents()
components.day = 31

使用精確匹配會在三月找到下個 31 號,如下:

calendar.nextDateAfterDate(valentines, matchingComponents: components, options: .MatchStrictly)
// Mar 31, 2015, 12:00 AM

不使用精確匹配的話汰寓,nextDateAfterDate 方法會在找到匹配的指定天數(shù)前就在二月底停了下來吆寨,然后在下個月繼續(xù)尋找。 可見踩寇,你所提供的選項決定了最終返回的具體日期啄清。舉例來說,使用 .MatchNextTime 選項找到下一個合適的日子:

calendar.nextDateAfterDate(valentines, matchingComponents: components, options: .MatchNextTime)
// Mar 1, 2015, 12:00 AM

類似的,當(dāng)使用 .MatchNextTimePreservingSmallerUnits 選項時會找到下一天辣卒,但是所有比指定單元 NSCalendarUnitDay 要小的單元會被保留下來:

calendar.nextDateAfterDate(valentines, matchingComponents: components, options: .MatchNextTimePreservingSmallerUnits)
// Mar 1, 2015, 9:00 AM

最后掷贾, 使用 .MatchPreviousTimePreservingSmallerUnits 選項會在 另一個 方向上解決缺失的時間問題, 和前面一樣荣茫,保留較小的單元想帅,然后找到匹配的前一天:

calendar.nextDateAfterDate(valentines, matchingComponents: components, options: .MatchPreviousTimePreservingSmallerUnits)
// Feb 28, 2015, 9:00 AM

除了這里的 NDateComponents 外,還值得注意的是 nextDateAfterDate 方法有兩種變化:

// 匹配指定的日歷單元
cal.nextDateAfterDate(valentines, matchingUnit: .CalendarUnitDay, value: 31, options: .MatchStrictly)
// March 31, 2015, 12:00 AM

// 匹配時啡莉,分港准,秒
cal.nextDateAfterDate(valentines, matchingHour: 15, minute: 30, second: 0, options: .MatchNextTime)
// Feb 14, 2015, 3:30 PM

枚舉插值日期

NSCalendar 提供了一個API去枚舉日期, 所以大家沒有必要反復(fù)的調(diào)用 nextDateAfterDate 方法咧欣。enumerateDatesStartingAfterDate(_:matchingComponents:options:usingBlock:) 方法根據(jù)提供的日期組件和選項浅缸,依次獲取匹配的日期∑枪荆可以將 stop 屬性設(shè)為 true 去停止枚舉衩椒。

來試試這個 NSCalendarOptions 的新方法吧,下面展示了一種獲取隨后50個閏年的方法

let leapYearComponents = NSDateComponents()
leapYearComponents.month = 2
leapYearComponents.day = 29

var dateCount = 0
cal.enumerateDatesStartingAfterDate(NSDate(), matchingComponents: leapYearComponents, options: .MatchStrictly | .SearchBackwards)
{ (date: NSDate!, exactMatch: Bool, stop: UnsafeMutablePointer<ObjCBool>) in
println(date)

if ++dateCount == 50 {
    // .memory 用來獲取一個 UnsafeMutablePointer 屬性的值
    stop.memory = true
}

}
// 2012-02-29 05:00:00 +0000
// 2008-02-29 05:00:00 +0000
// 2004-02-29 05:00:00 +0000
// 2000-02-29 05:00:00 +0000
// ...

處理周末

要想找周末的話哮兰,記住下面兩個 NSCalendar 方法就行:

  • nextWeekendStartDate(_:interval:options:afterDate): 根據(jù)傳入的前兩個參數(shù)返回下個周末的開始時間和長度毛萌。如果當(dāng)前的地區(qū)和日歷未提供對周末屬性的支持,該方法會返回 false 喝滞。唯一相關(guān)的屬性是 .SearchBackwards阁将。(例子在下面。)
  • rangeOfWeekendStartDate(_:interval:containingDate): 根據(jù)傳入的前兩個參數(shù)返回 包含 該日期的周末右遭。如果傳入的日期并不在周末或者當(dāng)前的地區(qū)和日歷未提供對周末屬性的支持做盅,該方法會返回 false 。

本地化的日歷符號

就好像所有的新功能還不夠狸演,NSCalendar還可以訪問一整套正確本地化的日歷符號言蛇,從而可以快速訪問月份名稱僻他,一周中的幾天等等宵距。每一組符號進(jìn)一步列舉在兩個軸上:(1)符號的長度和(2)它作為獨立的名詞或作為日期的一部分使用。

理解這個第二個屬性對于本地化是非常重要的吨拗,因為一些語言满哪,尤其是斯拉夫語言,對不同的上下文使用不同的名詞用例劝篷。例如哨鸭,日歷需要standaloneMonthSymbols為其標(biāo)題使用其中一種變體,而不是monthSymbols用于格式化特定日期的變體娇妓。

為了您的細(xì)讀像鸡,下面NSCalendar是俄文專欄中獨立符號的不同值的所有可用符號表

en_US ru_RU
monthSymbols January, February, March… января, февраля, марта…
shortMonthSymbols Jan, Feb, Mar… янв., февр., марта…
veryShortMonthSymbols J, F, M, A… Я, Ф, М, А…
standaloneMonthSymbols January, February, March… Январь, Февраль, Март…
shortStandaloneMonthSymbols Jan, Feb, Mar… Янв., Февр., Март…
veryShortStandaloneMonthSymbols J, F, M, A… Я, Ф, М, А…
weekdaySymbols Sunday, Monday, Tuesday, Wednesday… воскресенье, понедельник, вторник, среда…
shortWeekdaySymbols Sun, Mon, Tue, Wed… вс, пн, вт, ср…
veryShortWeekdaySymbols S, M, T, W… вс, пн, вт, ср…
standaloneWeekdaySymbols Sunday, Monday, Tuesday, Wednesday… Воскресенье, Понедельник, Вторник, Среда…
shortStandaloneWeekdaySymbols Sun, Mon, Tue, Wed… Вс, Пн, Вт, Ср…
veryShortStandaloneWeekdaySymbols S, M, T, W… В, П, В, С…
AMSymbol AM AM
PMSymbol PM PM
quarterSymbols 1st quarter, 2nd quarter, 3rd quarter, 4th quarter 1-й квартал, 2-й квартал, 3-й квартал, 4-й квартал
shortQuarterSymbols Q1, Q2, Q3, Q4 1-й кв., 2-й кв., 3-й кв., 4-й кв.
standaloneQuarterSymbols 1st quarter, 2nd quarter, 3rd quarter, 4th quarter 1-й квартал, 2-й квартал, 3-й квартал, 4-й квартал
shortStandaloneQuarterSymbols Q1, Q2, Q3, Q4 1-й кв., 2-й кв., 3-й кв., 4-й кв.
eraSymbols BC, AD до н. э., н. э.
longEraSymbols Before Christ, Anno Domini до н.э., н.э.

注: 這些符號在 NSDateFormatter 中也可以使用。

小知識

這里給大家介紹一個非常好用的 NSCalendar 擴(kuò)展集( 點 我 )哈恰,有了它我們使用訪問日期組件和搜索周末方法時只估,可以不用把值傳進(jìn)又傳出志群。比如,獲取指定的日期組件就變得簡單的多:

// built-in
var hour = 0
var minute = 0
calendar.getHour(&hour, minute: &minute, second: nil, nanosecond: nil, fromDate: NSDate())

// Swift化
let (hour, minute, _, _) = calendar.getTimeFromDate(NSDate())

獲取下一個周末的日期范圍:

// built-in
var startDate: NSDate?
var interval: NSTimeInterval = 0
let success = cal.nextWeekendStartDate(&startDate, interval: &interval, options: nil, afterDate: NSDate())
if success, let startDate = startDate {
    println("start: \(startDate), interval: \(interval)")
}

// Swift化
if let nextWeekend = cal.nextWeekendAfterDate(NSDate()) {
    println("start: \(nextWeekend.startDate), interval: \(nextWeekend.interval)")
}

這下復(fù)雜的日歷計算嚇不到你們了蛔钙。有了 NSCalendar 提供的這些新功能锌云,你可以很快的解決你碰到的問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吁脱,一起剝皮案震驚了整個濱河市桑涎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兼贡,老刑警劉巖攻冷,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異紧显,居然都是意外死亡讲衫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門孵班,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涉兽,“玉大人,你說我怎么就攤上這事篙程〖衔罚” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵虱饿,是天一觀的道長拥诡。 經(jīng)常有香客問我,道長氮发,這世上最難降的妖魔是什么渴肉? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮爽冕,結(jié)果婚禮上仇祭,老公的妹妹穿的比我還像新娘。我一直安慰自己颈畸,他們只是感情好乌奇,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眯娱,像睡著了一般礁苗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上徙缴,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天试伙,我揣著相機(jī)與錄音,去河邊找鬼。 笑死疏叨,一個胖子當(dāng)著我的面吹牛吱抚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播考廉,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼秘豹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昌粤?” 一聲冷哼從身側(cè)響起既绕,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涮坐,沒想到半個月后凄贩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡袱讹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年疲扎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捷雕。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡椒丧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出救巷,到底是詐尸還是另有隱情壶熏,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布浦译,位于F島的核電站棒假,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏精盅。R本人自食惡果不足惜帽哑,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叹俏。 院中可真熱鬧妻枕,春花似錦、人聲如沸她肯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晴氨。三九已至,卻和暖如春碉输,著一層夾襖步出監(jiān)牢的瞬間籽前,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留枝哄,地道東北人肄梨。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像挠锥,于是被迫代替她去往敵國和親众羡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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