内容简介:卡顿必须要优化,于是在网上找到了这样几个判断日期是否是同一天的方法,如下:从上面结果可以看出,方法4比前三个方法要好不少,尤其是要比较的两个日期不是同一天的情况下。方法3是系统API,其性能也比前两种方法好。既然判断同一天的方法如此高效,那是否能够用来判断两个日期是否是同一年呢?
很多同学都写过判断两个日期是否是同一天或者同一年这样的方法。方法需求很简单,直接使用系统库的方法来判断就可以了。近期做了这样的一个UI界面,如图,所有图片通过拍摄时间以天为单位分成一个个大组,然后,再按拍摄地点分成小组;然后同一年的第一个section节头上要突出显示年份。但是,界面在首次滑动时,总有卡顿感。使用Xcode自带的 Instruments’ Time Profiler 测量了下时间,主要耗时居然是判断日期是否是同一天的方法。
判断日期是否是同一天的几个方法
卡顿必须要优化,于是在网上找到了这样几个判断日期是否是同一天的方法,如下:
-
格式成日期字符串后比较
func isSameDay1(_ date1:Date, _ date2:Date) -> Bool { let formatter = DateFormatter() formatter.dateFormat = "yyyyMMdd" return formatter.string(from: date1) == formatter.string(from: date2) }
-
取出日期的年月日比较
func isSameDay2(_ date1:Date, _ date2:Date) -> Bool { let calendar = Calendar.current let d1 = calendar.dateComponents([.year,.month,.day], from: date1) let d2 = calendar.dateComponents([.year,.month,.day], from: date2) return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day }
-
使用Calendar的isDate(_:Date, inSameDayAs:Date)方法进行判断 这个方法是最简单的,直接使用系统库的方法,不用写其他代码
func isSameDay3(_ date1:Date, _ date2:Date) -> Bool { return Calendar.current.isDate(date1, inSameDayAs: date2) }
笔者在试过这几个方法后,发现主要耗时还是在判断是否是同一天的方法上。没办法,图片分组时,必须要判断是否是同一天,这个方法是绕不过去的,于是硬着头皮找到另外一种判断是否是同一天的方法,如下:
-
直接通过天数来判断是否是同一天
时间戳是从1970年的1月1日开始的秒数。如果,两个时间戳相差超过一天,那它们肯定不是同一天;如果相差少于一天,则计算这两个时间戳在该时区的天数,比较天数是否相等。比如,在+0的时区,我们用时间戳直接除以864000,得到的整数就是天数,在+8时区,由于第一天开始的时间戳是 8*3600,所以我们计算天数的时候加上这个值就可以了。系统也给我们提供了这个方法secondsFromGMT()来获取不同时区与标准时区的时间戳差值。
func isSameDay4(_ date1:Date, _ date2:Date) -> Bool { let secondesPerDay:Int = 24 * 3600 let t1:Int = Int(date1.timeIntervalSince1970) let t2:Int = Int(date2.timeIntervalSince1970) if abs(t1 - t2) > secondesPerDay { return false } let offset:Int = Calendar.current.timeZone.secondsFromGMT() return (t1 + offset) / (secondesPerDay) == (t2 + offset) / (secondesPerDay) }
几种方法对比
在iPhoneX上执行了20000次比较,结果如下:
两个日期是同一天的比较结果:
两个日期不是同一天的比较结果:
从上面结果可以看出,方法4比前三个方法要好不少,尤其是要比较的两个日期不是同一天的情况下。方法3是系统API,其性能也比前两种方法好。
引申问题
既然判断同一天的方法如此高效,那是否能够用来判断两个日期是否是同一年呢?
同一天可以通过这个方法判断,是因为没有闰秒,也就是说一天总是3600*24秒(在实际当中是有闰秒的,但没有规律,POSIX的计时没有算闰秒),而一年的时间不是固定的365天,所以这个方法不能简单的套用。但是可以通过日期当前的年份数,再比较年份数就可以了。代码如下:
class func getYear(_ date:Date?) -> Int? { guard let date = date else { return nil } let timeInterval:TimeInterval = date.timeIntervalSince1970 let secondsFromGMT = Calendar.current.timeZone.secondsFromGMT() // 如果时间戳为负,或者在32位机器上超过Int类型最大值,直接调用系统的获取年份的方法 // 下面的方法对于时间戳为负也是可以处理的 // 这里考虑到大部分日期都是1970年以后,对于1970年以前的日期就直接调用系统方法进行处理 if timeInterval >= 0 && timeInterval < Double(Int.max) { var days:Int = (Int(timeInterval) + secondsFromGMT ) / 86400 var year:Int = 1970 while (days < 0 || days >= (isLeapYear(year) ? 366 : 365)) { var guessYear:Int = 0 if days >= 0 { guessYear = year + days/365 } else { guessYear = year - (abs(days)/365 + 1) } days -= (guessYear - year) * 365 + leapsEndOfYear(guessYear - 1) - leapsEndOfYear(year - 1) year = guessYear } return year } return Calendar.current.dateComponents([.year,.month,.day], from: date).year } /// 获取该年份前所有闰年数 class func leapsEndOfYear(_ year:Int) -> Int { return year/4 - year/100 + year/400 } /// 判断闰年 能过被4整除,但不能被100整除;或者能被400整除的年份 class func isLeapYear(_ year:Int) -> Bool { return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) }
大致思路是,先将时间戳转换为该时区的天数,再通过每年365天猜测一个年份,算出该年份的天数,然后用实际天数减去猜测的年份的天数,计算出剩余的天数,如果剩余天数少于该猜测的年份的天数(365或者366)则退出循环,返回该猜测的年份 如果剩余天数大于该猜测的年份的天数(365或者366),则再对剩余的天数进行同样的处理。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 根据输入的日期(年月日)判断是星期几——golang实现
- mysql 获取昨天日期、今天日期、明天日期以及前一个小时和后一个小时的时间
- AYUI内置的万能日期控件-日期表达式
- oracle 日期格式化(yyyymmdd)及常规日期计算大全
- ElasticSearch中的日期映射为Hive中的日期格式
- 将日期时间列表与Python中的日期时间进行比较
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
疯狂科学家大本营
Bei Er Fei Ao Er / 本书翻译组 译、黄晓庆 周宇煜 张为民 审译 / Science Press / 2012-1-5 / 48.00元
美国最棒的创意工场不是贝尔实验室,不是硅谷,也不是麻省理工学院的媒体实验室,而是由五角大楼领导的绝密军事机构DARPA——国防高级研究计划局。DARPA是由美国前总统艾森豪威尔建立的军事部门,创建的目的是为了回应苏联的太空计划。 虽然DARPA属于政府机构,但是没有冷冰 冰的氛围和官僚做派,那里的科学家偏爱牛仔裤和运动鞋。不过他们最爱的还是在各个领域寻找颠覆性创意。从航空航天、IT,到能源领......一起来看看 《疯狂科学家大本营》 这本书的介绍吧!