序言
本文将用到的科普知识如下:
场景描述
近来开发过程中QA朋友提了一个bug,当手机日期时间更改后发觉页面时间显示异常,这些问题特别精典,也就是iOS关于时间的处理.
我们对时间的认识
时间是线性的,即任意一个时刻,这个月球上只有一个绝对时间值存在,只不过由于时区或则文化的差别adobe air linux,处于同一时空的我们对同一时间的叙述或则理解不同。例如,上海的20:00和东京的21:00虽然是同一个绝对的时间值。
可以理解为以一个标准点作为标准点.通过时区微调来实现全球各个国家的日期显示.iOS几种获取时间的方法1.NSDate
代码实现
(void)timeIntervalSinceReferenceDate {
NSDate *date = [NSDate date];
NSLog(@"date = %lf", date.timeIntervalSinceReferenceDate);
}
NSDate对象封装单个时间点,与任何特定的月历系统或时区无关.日期对象是不可变的,表示相对于绝对参考日期(2001年1月1日00:00:00UTC)的不变时间间隔,它是以UTC为标准的。
NSDate输出结果:
2020-12-06 12:28:55.795929+0800 ZGTimeDemo[12177:134289] date = 628921735.795845
下边估算一下:628921735.795845/365/86400=19.942977,明年是2020年,距离2001年刚好是19年.
假如我们直接复印NSDate
NSDate *date = [NSDate date];
NSLog(@"%@",date);
则会输出
2020-12-06 06:51:04 +0000
可见NSDate输出的是绝对的UTC时间,而上海时间的时区为UTC+8,里面的输出+8个小时,正好就是我当前的时间了。所以正常UTC+时区才是真正的时间日期.至于时区加减请参考右图.
注意:NSDate是受手机系统时间控制的,当你更改了手机上的时间显示,NSDate获取当前时间的输出也会急剧改变。在我们做App的时侯linux视频教程,明白这一点ios unix时间戳转换成时间ios unix时间戳转换成时间,就晓得NSDate并不可靠,由于用户可能会更改它的值.
2.函数CFAbsoluteTimeGetCurrent()官方文档:绝对时间是相对于绝对参考日期(格林尼治标准时间2001年1月1日00时00分)以秒估算的。正值表示引用日期以后的日期,负值表示引用日期之前的日期。诸如,绝对时间-32940326相当于1999年12月16日17:54:34。重复调用这个函数不能保证单调递增的结果。系统时间可能因为与外部时间引用同步或因为显式的用户修改时钟而降低。
CFAbsoluteTimeGetCurrent()的概念和NSDate十分相像,只不过参考点是:以GMT为标准的,2001年九月一日00:00:00这一刻的时间绝对值。
注意:CFAbsoluteTimeGetCurrent()也会跟随当前设备的系统时间一起变化,也可能会被用户更改.
3.gettimeofday()
int gettimeofday(struct timeval * __restrict, void * __restrict);
这个函数获取的是UNIXtime.
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
NSLog(@"gettimeofday: %ld", now.tv_sec);
gettimeofday: 1607238723
UNIXtime又是哪些呢?
Unixtime是以UTC1970年1月1号00:00:00为基准时间,当前时间距离基准点偏斜的秒数。上述API返回的值是1607238723,表示当前时间距离UTC1970年1月1号00:00:00一共过了1607238723秒。
Unixtime也是平常我们使用较多的一个时间标准,在Mac的终端可以通过以下命令转换成可阅读的时间:
date -r 1607238723
输出
2020年12月 6日 星期日 15时12分03秒 CST
注意:gettimeofday(),NSDate,CFAbsoluteTimeGetCurrent这三个都是受当前设备的系统时间影响.只不过是参考的时间基准点不一样而已。我们和服务器通信的时侯通常使用NIXtime.
5.mach_absolute_time()
在我们的iPhone上恰好有一个这样的值存在,它就是CPU的时钟周期数(ticks),这个tick的数值可以拿来描述时间,而mach_absolute_time()返回的就是CPU早已运行的tick的数目。将这个tick数经过一定的转换就可以弄成秒数,或则毫秒数.这样就和时间直接关联了.不过这个tick数,在每次手机重启以后,会重新开始计数,但是iPhone屏保步入休眠以后tick也会暂停计数.
注意:mach_absolute_time()不会受系统时间影响,只受设备重启和休眠行为影响
6.CACurrentMediaTime()
CACurrentMediaTime()就是将前面mach_absolute_time()的CPUtick数转化成秒数的结果。以下代码:
double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);
'>2020-12-06 15:34:59.808799+0800 ZGTimeDemo[19731:281283] CACurrentMediaTime: 17789.582767
返回的就是开机后设备一共运行了(设备休眠不统计在内)多少秒.
这个API等同于下边代码:
NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
注意:CACurrentMediaTime()也不会受系统时间影响,只受设备重启和休眠行为影响.
7.sysctl()
iOS系统还记录了先前设备重启的时间。可以通过如下API调用获取:
#include - (long)bootTime
{
#define MIB_SIZE 2
int mib[MIB_SIZE];
size_t size;
struct timeval boottime;
mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
size = sizeof(boottime);
if (sysctl(mib, MIB_SIZE, &boottime, &size, NULL, 0) != -1)
{
return boottime.tv_sec;
}
return 0;
}
返回的值是先前设备重启的Unixtime。
注意:这个API返回的值也会受系统时间影响,用户假如更改时间,值也会随着变化.
顾客端和服务器之间的时间同步
通常我们发起恳求的时侯都是在公参中带上本地时间,假如有一些比较敏感的插口会碰到用户修改系统时间的异常case引起异常.为了避免用户通过断网更改系统时间,来影响顾客端的逻辑我们一般都这样做.
这儿要确切做到顾客端时间和服务器时间一致,很关键的问题就是B和C不能受系统时间的影响,要解决这个问题,要借助iOS的插口–系统运行时间
首先:我们要借助服务端给一个确切的时间戳.每次同步记录一个得到服务端时间戳B.我们就是要用运行的时间差来解决时间校时问题.
获取系统当前运行了多长时间方式:
//get system uptime since last boot
- (NSTimeInterval)uptime
{
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
double uptime = -1;
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
{
uptime = now.tv_sec - boottime.tv_sec;
uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0;
}
return uptime;
}
注意:这个函数返回的是秒.和server返回的unixtime可能要减去1000.(1s=1000ms)
gettimeofday()和sysctl()就会受系统时间影响,但她们两者做一个加法所得的值,就和系统时间无关了.这样就可以防止用户更改时间了。其实用户假如死机,过段时间再开机,会造成我们获取到的时间慢与服务器时间,真实场景中,慢于服务器时间常常影响较小,我们通常害怕的是顾客端时间快于服务器时间.
总结
本篇问题的解决难点的关键在于假如获取本地的时间,我们这儿取的是系统运行时间进行的差值估算法.我没有尝试过休眠后退台等逻辑消耗的时长.而且我觉得假如要做好工具类,要尝试估算后台消耗的时间计时时长,可以也可以通过系统运行时间的差值运算得到确切的时间.
本篇重点:ABCD同步时间算法主要依赖于服务端给的时间作为基准点.另一个难点如何获取系统运行时间做差值估算来解决系统时间被用户更改后时间不准的问题.
推荐选集