有一個多月沒有更新簡書了,因為工作確實比較忙,天天加班。現在APP已經上線,是時侯繼續我的寫作之旅了。這次項目里面會進行大量的日期時間操作。比如獲取某日期年月日,時分。兩個日期的比較。設置某個日期的時間等操作。總之這次開發過程中不友好
NSDate
和其相關類的API讓我感到不爽,感覺iOS原生的日期類非常不好用。于是我便萌生了自己寫一個關于操作NSDate
的類的想法,因以我以前是用C#,感覺C#的日期時間類比較優雅,我便參考它寫了個iOS的DateTime
,今天我便分享給大家。
iOS對日期和時間的操作的不足
NSDate
是iOS表示日期時間的核心類。但是它也只能表示時間,如果需要格式化輸出,需要使用到NSDateFormatter
類,而如果需要取出里面的年月日和時分秒,則需要用到NSDateComponents
和NSCalendar
。相信每一位iOS開發者都會掌握這些類的使用,這里就不寫示例代碼了。雖然職責分離是一種好的設計,但是需要你大量對日期時間的比較,格式化輸出等操作時,使用這些類來非常繁瑣,要寫很多的重復的代碼,影響了開發效率。GrandTime
正是解決這個問題而生的。
GrandTime組成
GrandTime
由三個類組成:DateTime
,TimeSpan
和GrandTimer
。DateTime
是最核心的類,封裝了NSDate
和NSDateComponents
。把年月日時分秒的操作變得十分簡單方便。而TimeSpan
是一段時間間隔,相當于NSTimeInterval
,同時還封裝了很多功能。而GrandTimer是一個會自動停止的弱Timer
,并且提供了Block
來方便調用。下面我一個一個來介紹怎么使用。
DateTime
DateTime
類封裝了常見的日期時間操作。它提供了如下8個構造函數
相信這8個構造函數可以滿足絕大部分的使用場景。里面有兩個需要說明一下,
tick
在里面是指毫秒,而timestamp
是Unix
時間戳。默認的無參構造函數和NSDate
一樣,都是使用當前時間。下面看看代碼
//正面看看構造函數
let a = DateTime() //直接初始化
print(a)
let c = DateTime(date: NSDate(timeInterval: 3600, sinceDate: NSDate())) //使用NSDate初始化
print(c)
let e = DateTime(tick: 1000000) //使用Tick初始化 從1970年開始
print(e)
let f = DateTime(tickSinceNow: 60000) //使用Tick初始化 從現在年開始
print(f)
let g = DateTime(timestamp: 100000)//使用Stamp初始化
print(g)
let h = DateTime(year: 2008, month: 2, day: 29) //使用年月日初始化
print(h)
let i = DateTime(year: 2016, month: 12, day: 12, hour: 11, minute: 44, second: 12, millisecond: 111)!//使用年月日時分秒毫秒初始化
print(i)
打印結果:
2016-07-18 15:04:37:779
2016-07-18 16:04:37:779
Optional(1970-01-01 08:16:40:000)//注意里面存在初始化失敗這種情況,我沒有直接拋出異常,而是返回可選值。
Optional(2016-07-18 15:05:37:780)
Optional(1970-01-02 11:46:40:000)
Optional(2008-02-29 00:00:00:000)
2016-12-12 11:44:12:000
我為DateTime
提供了操作符重載
let timeSpanOneMinute = TimeSpan.fromMinuts(1) //聲明一個一分鐘的TimeSpan
let dToOneMinuteBefore = a - timeSpanOneMinute // 一分鐘前
print("一分鐘前\(dToOneMinuteBefore)")
let dToOneMinuteAfter = a + timeSpanOneMinute // 一分鐘后
print("一分鐘后\(dToOneMinuteAfter)")
//兩個DateTime相減生成一個TimeSpan
let span = c - a
print("a和c相差一小時\(span)")
print("a>c:\(a>c)")
print("a<c:\(a<c)")
打印結果:
一分鐘前2016-07-18 15:14:10:274
一分鐘后2016-07-18 15:16:10:274
a和c相差一小時Optional(1:00:00:000)
a>c:false
a<c:true
DateTime
也提供了一系列Add
方法,可以對自身操作。改變自己的時間值。
a.addYears(1) //加一年
print("add Years:\(a)")
a.addMonth(1) // 加 一個月
print("add addMonth:\(a)")
a.addDays(1) // 加一天
print("add addDays:\(a)")
a.addHours(1) // 加一個小時
print("add addHours:\(a)")
a.addMinutes(1) // 加一分鐘
print("add addMinutes:\(a)")
a.addSeconds(1) // 加一秒
print("add addSeconds:\(a)")
打印結果:
add Years:2017-07-18 17:20:09:080
add addMonth:2017-08-18 17:20:09:080
add addDays:2017-08-19 17:20:09:080
add addHours:2017-08-19 18:20:09:080
add addMinutes:2017-08-19 18:21:09:080
add addSeconds:2017-08-19 18:21:10:080
使用DateTime
來獲取日期時間的每個部分也是非常簡單,同時也支持單獨設值
print("獲取i的各部分:year:\(i.year), month:\(i.month), day:\(i.day), hour:\(i.hour), minute:\(i.minute), second:\(i.second), minute:\(i.minute), ticks:\(i.ticks), ")
//還可以直接設置各部分
i.year = 2015
i.month = 1
i.day = 12
i.hour = 12
i.minute = 23
i.second = 12
i.millisecond = 555
print("再次獲取i的各部分:year:\(i.year), month:\(i.month), day:\(i.day), hour:\(i.hour), minute:\(i.minute), second:\(i.second), minute:\(i.minute), ticks:\(i.ticks), ")
//打印結果
獲取i的各部分:year:2016, month:12, day:12, hour:11, minute:44, second:12, minute:44, ticks:1481514252000,
再次獲取i的各部分:year:2015, month:1, day:12, hour:12, minute:23, second:12, minute:23, ticks:1421036592555,
//獲取季度和星期相關數據
print("星期幾:\(i.weekDay)")
print("第幾季度:\(i.quarter)")
print("一年的第幾周:\(i.weekOfYear)")
print("一個月的第幾周:\(i.weekOfMonth)")
print("一年的第幾天:\(i.dayOfYear)")
//打印結果
星期幾:Wendesday
第幾季度:0
一年的第幾周:3
一個月的第幾周:3
一年的第幾天:43
最后就是格式化輸出了,DateTime
使用了一個單例的NSDateFormatter
,這樣可以防止不必要的頻繁實例化NSDateFormatter
。下面看看輸出
print("獲取日期部分\(i.dateString)") //獲取日期部分
print("獲取時間部分\(i.timeString)") //獲取時間部分
print("默認格式\(i.format())")
print("自定義格式\(i.format("yyyy年MM月dd日#EEEE"))")
print("各種輸出style的原生的一樣")
print("LongStyle: \(i.format(.ShortStyle, timeFormat: .ShortStyle))")
print("LongStyle: \(i.format(.MediumStyle, timeFormat: .MediumStyle))")
print("LongStyle: \(i.format(.LongStyle, timeFormat: .LongStyle))")
print("LongStyle: \(i.format(.FullStyle, timeFormat: .FullStyle))")
i.local = NSLocale(localeIdentifier: "en_US")
print("把地區設為US"),
print("LongStyle: \(i.format(.ShortStyle, timeFormat: .ShortStyle))")
print("LongStyle: \(i.format(.MediumStyle, timeFormat: .MediumStyle))")
print("LongStyle: \(i.format(.LongStyle, timeFormat: .LongStyle))")
print("LongStyle: \(i.format(.FullStyle, timeFormat: .FullStyle))")
//打印結果
獲取日期部分2015-01-12
獲取時間部分12:23:12
默認格式2015-01-12 12:23:12:555
自定義格式2015年01月12日#Monday
各種輸出style的原生的一樣
LongStyle: 15/1/12 下午12:23
LongStyle: 2015年1月12日 下午12:23:12
LongStyle: 2015年1月12日 GMT+8 下午12:23:12
LongStyle: 2015年1月12日 星期一 中國標準時間 下午12:23:12
把地區設為US
LongStyle: 1/12/15, 12:23 PM
LongStyle: Jan 12, 2015, 12:23:12 PM
LongStyle: January 12, 2015 at 12:23:12 PM GMT+8
LongStyle: Monday, January 12, 2015 at 12:23:12 PM China Standard Time
DateTime
的用法在上面的代碼基本可以全部找到。更多的用法請參考原代碼或者API。可見,使用DateTime
還操作時間還是很方便的。
TimeSpan
同樣,為了更好地使用時間間隔,我寫了TimeSpan
來專門處理時間間隔。TimeSpan
類比較簡單一點,下面直接上代碼
//先看看構造函數
let o = TimeSpan() //直接初始化
print(o)
let p = TimeSpan(days: 1, hours: 0, minutes: 11, seconds: 31) //使用天,小時,分鐘,秒來初始化
print(p)
let q = TimeSpan(days: 20, hours: 11, minutes: 39, seconds: 21, milliseconds: 111)! //使用天,小時,分鐘,秒還有來初始化
print(q)
let r = TimeSpan(ticks: 9826127) //使用tick來初始化
print(r)
//打印結果
0:00:00:000 //全是0
Optional(1 0:11:31:000) //1天11分鐘31秒
20 11:39:21:111 //20天11小時39分鐘21秒111毫秒
2:43:46:127 //2小時43分鐘46秒127毫秒
TimeSpan
的構造函數不多,也比較簡單。里面最大的時間間隔單位是天這個單位。因為天可以一直累加,所以也足夠用了。同樣,使用from
系列靜態函數也可以自己想要的時間間隔,
從圖中可以看出,很多函數所傳遞的變量是Double類型的, 也就是說,可以給它傳小數值。下面看看代碼吧
//使用from 函數
print("使用from函數")
var s = TimeSpan.fromDays(1) //一天
print(s)
s = TimeSpan.fromHours(2.5) //2.5小時
print(s)
s = TimeSpan.fromMinuts(89.2)//89.2分鐘
print(s)
s = TimeSpan.fromSeconds(134)//134秒
print(s)
s = TimeSpan.fromTicks(123123123)//123123123 tick
//打印結果
使用from函數
1 0:00:00:000
2:30:00:000
1:29:12:000
0:02:14:000
1 10:12:03:123
可以單獨取和設定某個屬性
//下面獲取部分
print("獲取i的各部分: day:\(q.days), hour:\(q.hours), minute:\(q.minutes), second:\(q.seconds), minute:\(q.milliseconds), ticks:\(q.ticks), ")
//獲取計算的總體部分
print("獲取i的各部分: totalDays:\(q.totalDays), totalHours:\(q.totalHours), totalMinutes:\(q.totalMinutes), second:\(q.totalSeconds)")
//單獨設定屬性
q.days = 4
q.hours = 22
q.minutes = 12
q.seconds = 32
q.milliseconds = 343
print("獲取i的各部分: day:\(q.days), hour:\(q.hours), minute:\(q.minutes), second:\(q.seconds), minute:\(q.milliseconds), ticks:\(q.ticks), ")
print("獲取i的各部分: totalDays:\(q.totalDays), totalHours:\(q.totalHours), totalMinutes:\(q.totalMinutes), second:\(q.totalSeconds)")
//打印結果
獲取i的各部分: day:20, hour:11, minute:39, second:21, minute:111, ticks:1769961111,
獲取i的各部分: totalDays:20.4856610069444, totalHours:491.655864166667, totalMinutes:29499.35185, second:1769961.111
獲取i的各部分: day:4, hour:22, minute:12, second:32, minute:343, ticks:426239657,
獲取i的各部分: totalDays:4.93332936342593, totalHours:118.399904722222, totalMinutes:7103.99428333333, second:426239.657
和DateTime
一樣,重載了運算符號來實現TimeSpan
之間也的加減操作,
print("下面看加減")
s = s.add(r) //可以用這個加
print(s)
s = s.subtract(r) //可以用這個減
print(s)
print("運算符+ - 也一樣")
s = s + r
print(s)
s = s - r
print(s)
打印結果
下面看加減
1 12:55:49:250
1 10:12:03:123
運算符+ - 也一樣
1 12:55:49:250
1 10:12:03:123
OK, 上面就是DateTime
和TimeSpan
的各種用法,相信它們可以滿足大部分項目的需求,如果不夠,自己也可以寫擴展來實現。
GrandTime
還提供了一個計時器GrandTimer
。和NSTimer
不一樣,GrandTimer
不會強引用當前類。所以不會出現NSTimer
那種只要不調用valifdate()
方法就會在內存里一直運行這種情況。使用GrandTimer
非常簡單,直接使用其靜態方法即可
weak var weakSelf = self
//使用者要注意,這個timer本身是weak的,所以需要一個外部變量來強引用, 不然出這代碼區域,就會被內存回收導致計時器不能運行
//如果要讓timer正確地釋放內存,那么要使用weakself
timer = GrandTimer.every(TimeSpan.fromSeconds(1)) {
weakSelf!.seco2 = weakSelf!.seco2 + 1
weakSelf!.lblTimer.text = "\(weakSelf!.seco2)"
}
//如果要暫停
timer?.invalidate()
// 重寫了該ViewController的deinit
deinit{
print("\(self.dynamicType)) the view deinit which means the timer release in the viewcontrollre")
}
//如果pop該ViewController出去,這個時侯會打印
TimerViewController) the view deinit which means the timer release in the viewcontrollre
//說明該View已經正確地被內存釋放了
GrandTimer也提供了一個selector版本,不過需要自己提供一個線程,這種用法用NSTimer很類似。
let dispatch = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT)
timer = GrandTimer.scheduleTimerWithTimeSpan(TimeSpan.fromSeconds(1), target: self, sel: #selector(TimerViewController.tick), userInfo: nil, repeats: true, dispatchQueue: dispatch)
timer?.fire()
以上就是GrandTime
的所有功能的展示的,我已經將這個項目加入了Cocoapods
。 使用起來非常簡單,直接pod‘GrandTime’
就可以啦,如果你不想用Cocoapods
,那么也可以將里面的文件直接拖到項目里面。Git地址:GrandTime。 如果你們喜歡的話,麻煩給個Star。