GrandTime--操作iOS的时间更简单

例行上图

有一个多月没有更新简书了,因为工作确实比较忙,天天加班。现在APP已经上线,是时侯继续我的写作之旅了。这次项目里面会进行大量的日期时间操作。比如获取某日期年月日,时分。两个日期的比较。设置某个日期的时间等操作。总之这次开发过程中不友好NSDate和其相关类的API让我感到不爽,感觉iOS原生的日期类非常不好用。于是我便萌生了自己写一个关于操作NSDate的类的想法,因以我以前是用C#,感觉C#的日期时间类比较优雅,我便参考它写了个iOS的DateTime,今天我便分享给大家。

iOS对日期和时间的操作的不足

NSDate是iOS表示日期时间的核心类。但是它也只能表示时间,如果需要格式化输出,需要使用到NSDateFormatter类,而如果需要取出里面的年月日和时分秒,则需要用到NSDateComponentsNSCalendar。相信每一位iOS开发者都会掌握这些类的使用,这里就不写示例代码了。虽然职责分离是一种好的设计,但是需要你大量对日期时间的比较,格式化输出等操作时,使用这些类来非常繁琐,要写很多的重复的代码,影响了开发效率。GrandTime正是解决这个问题而生的。

GrandTime组成

GrandTime由三个类组成:DateTime,TimeSpanGrandTimerDateTime是最核心的类,封装了NSDateNSDateComponents。把年月日时分秒的操作变得十分简单方便。而TimeSpan是一段时间间隔,相当于NSTimeInterval,同时还封装了很多功能。而GrandTimer是一个会自动停止的弱Timer,并且提供了Block来方便调用。下面我一个一个来介绍怎么使用。

DateTime

DateTime类封装了常见的日期时间操作。它提供了如下8个构造函数

DateTime的构造函数

相信这8个构造函数可以满足绝大部分的使用场景。里面有两个需要说明一下,tick在里面是指毫秒,而timestampUnix时间戳。默认的无参构造函数和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方法,可以对自身操作。改变自己的时间值。

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系列静态函数也可以自己想要的时间间隔,

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, 上面就是DateTimeTimeSpan的各种用法,相信它们可以满足大部分项目的需求,如果不够,自己也可以写扩展来实现。

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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容