GrandTime--操作iOS的时间更简单
有一个多月没有更新简书了,因为工作确实比较忙,天天加班。现在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。