前言
最近项目的编写中,接口需要提交精确到秒级的时间戳用作校验。但是仅靠new Date().timeIntervalSince1970
会面临着本地的时间与服务器时间不一致的问题。那么本文方案能让本地应用时间与服务器时间存在误差范围内保持同步,减少应用出错率。
预备知识
- 获取设备当前时间
Now
,该值受系统时间影响,用户如果修改时间,值也会随着变化; - 获取设备上次重启的时间
BootTime
,该值受系统时间影响,用户如果修改时间,值也会随着变化; - 由上面
iOS
提供的两个接口,我们可以获取到设备自上次重启后运行的时间(BootTime - Now
),该值与系统时间无关;`-`这个不是减,指的是区间,下面同理 - 在必要的时刻获取一下服务器时间,然后记录这个时刻的设备自上次重启后运行的时间(
BootTime - Now
) - 后续时间获取:现在服务器时间 = 以前服务器时间 + 现在设备自上次重启后运行的时间 - 以前服务器时间的获取时刻的设备自上次重启后运行的时间
- 利用AFNetworking自动同步时间
时间的获取
获取now的Unix Time
1 | class func now() -> Int { |
获取设备上次重启的 Unix Time
1 | class func boottime() -> Int { |
获取设备自上次重启后运行的时间
1 | now() - boottime() |
oc获取方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 > //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;
> }
>
TimeUtils编写
1 | import UIKit |
利用AFNetworking同步时间
利用AFN发起请求返回时通过HTTP Header来获取服务器时间,时间格式以RFC-7231中定义的”HTTP日期”格式来发送,解析该时间需要用到以下的OC代码,Swift项目请自行用Bridging-Header
NSDate+InternetDateTime.h
1 | // |
NSDate+InternetDateTime.m
1 | // |
来源:https://github.com/mwaterfall/MWFeedParser/blob/master/Classes/NSDate+InternetDateTime.m(ARC)
在项目中封装一个AFN的请求基类
1 | import UIKit |
该方案下前提必须禁止AFN自带的缓存策略client.requestSerializer.cachePolicy = .reloadIgnoringLocalCacheData
。不禁止会出现时间异常的现象,如项目中是使用AFN自带的缓存策略则需要更换项目中的缓存方案。
该方案都仅到秒级,如需到毫秒级请做*1000处理
不足:连接服务器的过程是需要时间的,服务器收到请求时刻的时间与应用收到响应存在一定的时间差,导致误差的存在(误差=服务器发出响应->到本机收到响应这个时间)。
但是通过上面的AFN每次判断,可以使得误差逐渐降低
参考资料
https://www.jianshu.com/p/48ac0bb1392e