iOS关于时间的处理(一)—— 有关时间的基本知识

版本记录

版本号 时间
V1.0 2017.08.30

前言

app一定会和时间打交道,关于时间的处理有很多值得我们研究和注意的地方,时间是线性的,地球上各个时区的时间表示不一样,但是它们的绝对时间值是一样的,只是处于同一个时空我们对同一时间的表述或者理解不同罢了,这几篇就说一下关于时间的问题。

两种时间参考

1. GMT

人类对于时间的理解还很有限,但我们至少能确定一点:时间的变化是匀速的。时间前进的速度是均匀的,不会忽快忽慢,所以为了描述时间,我们也需要找到一个值,它的变化也是以均匀的速度向前变化的。

时间是线性匀速的,长久以来人们也是在找时间的参考物,前人发现抬头看太阳是个好办法,太阳总是按规律的“早起晚落”,而且“亘古不变”,可以用太阳在一天当中所处的位置来描述当前的时间。后来不同地区的文化需要交流,你这里太阳正高空照,我这可能已经下山了,所以需要有一个公共的大家都认可的地方,以这个地方太阳的位置来做参考着,沟通起来就会方便很多。最后选择的是英国伦敦的格林尼治天文台所在地,以格林尼治的时间作为公共时间,也就是我们所说的GMT时间(Greenwich Mean Time)

2. UTC

太阳所处的位置变化跟地球的自转相关,过去人们认为地球自转的速率是恒定的,但在1960年这一认知被推翻了,人们发现地球自转的速率正变得越来越慢,而时间前进的速率还是恒定的,所以GMT不再被认为可以用来精准的描述时间了。

我们需要继续寻找一个匀速前进的值。抬头看天是我们从宏观方向去寻找答案,科技的发展让我们在微观方面取得了更深的认识,于是有聪明人根据微观粒子原子的物理属性,建立了原子钟,以这种原子钟来衡量时间的变化,原子钟50亿年才会误差1秒,这种精读已经远胜于GMT了。这个原子钟所反映的时间,也就是我们现在所使用的UTC(Coordinated Universal Time )标准时间。


几种获取时间的方式

1. NSDate

先看一下它的开发文档。

NSDate objects encapsulate a single point in time, independent of any particular calendrical system or time zone. Date objects are immutable, representing an invariant time interval relative to an absolute reference date (00:00:00 UTC on 1 January 2001)

上面那一段翻译过来就是:NSDate对象封装单个时间点,与任何特定的日历系统或时区无关。 日期对象是不可变的,表示相对于绝对参考日期(2001年1月1日00:00:00 UTC)的不变时间间隔,它是以UTC为标准的。

下面看一下代码

- (void)demoTimeIntervalSinceReferenceDate
{
    NSDate *date = [NSDate date];
    NSLog(@"date = %lf", date.timeIntervalSinceReferenceDate);
}

下面看输出结果

2017-08-30 15:41:13.225921+0800 JJOC[1294:766001] date = 525771673.225875

下面我们计算一下: 525771673.225875/365/86400 = 16.6721104,今年是2017年,距离2001年正好是16年。

我们再看下面这个例子

- (void)demoDate
{
    NSDate *date = [NSDate date];
    NSLog(@"date = %@", date);
}

下面看输出结果

2017-08-30 15:44:54.197111+0800 JJOC[1301:767209] date = Wed Aug 30 15:44:54 2017

可见我这里输出的就是我当前的时间。

这里还要注意:NSDate是受手机系统时间控制的。也就是说,当你修改了手机上的时间显示,NSDate获取当前时间的输出也会随之改变。在我们做App的时候,明白这一点,就知道NSDate并不可靠,因为用户可能会修改它的值。

2. 函数CFAbsoluteTimeGetCurrent()

该函数出自Core Foundation框架,我们先看一下官方文档中的说明。

Absolute time is measured in seconds relative to the absolute reference date of Jan 1 2001 00:00:00 GMT. A positive value represents a date after the reference date, a negative value represents a date before it. For example, the absolute time -32940326 is equivalent to December 16th, 1999 at 17:54:34. Repeated calls to this function do not guarantee monotonically increasing results. The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.

这段文字翻译过来就是:相对于2001年1月1日00:00:00 GMT的绝对参考日期,绝对时间以秒为单位。 正值表示参考日期后的日期,负值表示其前的日期。 例如,绝对时间-32940326相当于1999年12月16日17:54:34。 对此功能的重复调用不能保证单调增加的结果。 由于与外部时间参考的同步或由于显式的用户更改时钟,系统时间可能会减少。

这里还要注意:CFAbsoluteTimeGetCurrent()也会跟着当前设备的系统时间一起变化,也可能会被用户修改。

3. 函数gettimeofday

还是先看一下API

int gettimeofday(struct timeval * __restrict, void * __restrict);
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
NSLog(@"gettimeofday: %ld", now.tv_sec);

该函数获取的时间是Unix time

Unix time是以UTC 1970年1月1号 00:00:00为基准时间,当前时间距离基准点偏移的秒数。

其实NSDate中也提供了获取这个时间的接口

- (void)demoTimeIntervalSince1970
{
    NSDate *date = [NSDate date];
    NSLog(@"date = %f", date.timeIntervalSince1970);
}

下面看输出结果

2017-08-30 16:02:54.282284+0800 JJOC[1307:770247] date = 1504080174.282267

同样,下面我们计算一下: 1504080174.282267/365/86400 = 47.694069444444445,今年是2017年,距离1970年正好是47年。

这里还要注意:gettimeofdayNSDateCFAbsoluteTimeGetCurrent()一样,都是受当前设备的系统时间影响。只不过是参考的时间基准点不一样而已。我们和服务器通讯的时候一般使用Unix time

4. mach_absolute_time()

我们需要找到一个均匀变化的属性值来描述时间,而在我们的iPhone上刚好有一个这样的值存在,就是CPU的时钟周期数(ticks)。这个tick的数值可以用来描述时间,而mach_absolute_time()返回的就是CPU已经运行的tick的数量。将这个tick数经过一定的转换就可以变成秒数,或者纳秒数,这样就和时间直接关联了。

不过这个tick数,在每次手机重启之后,会重新开始计数,而且iPhone锁屏进入休眠之后tick也会暂停计数。

这里要注意:mach_absolute_time()不会受系统时间影响,只受设备重启和休眠行为影响。

5. CACurrentMediaTime()

该函数是在QuartzCore框架中CABase.h中的函数。

/* Returns the current CoreAnimation absolute time. This is the result of
 * calling mach_absolute_time () and converting the units to seconds. */
CFTimeInterval CACurrentMediaTime (void)

CACurrentMediaTime()就是将上面mach_absolute_time()CPU tick数转化成秒数的结果。

下面看示例代码

- (void)demoCACurrentMediaTime
{
    double mediaTime = CACurrentMediaTime();
    NSLog(@"CACurrentMediaTime: %f", mediaTime);
}

下面看输出结果

2017-08-30 16:20:02.937803+0800 JJOC[1311:772292] CACurrentMediaTime: 194746.231819

这里输出的就是开机后设备一共运行了(设备休眠不统计在内)多少秒,另一个API也能返回相同的值。

- (void)demoSystemUptime
{
    NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
    NSLog(@"systemUptime: %f", systemUptime);
}

看输出结果

2017-08-30 16:22:42.853656+0800 JJOC[1314:772878] systemUptime: 194906.147670

这里要注意:CACurrentMediaTime()也不会受系统时间影响,只受设备重启和休眠行为影响。

6. sysctl

该函数来自于下面的文件。

下面看代码

#include <sys/sysctl.h>
- (void)demoBootTime
{
    #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)
    {
        NSLog(@"time = %ld", boottime.tv_sec);
    }
}

下面看输出结果

2017-08-30 16:29:49.043262+0800 JJOC[1318:773969] time = 1503235239

返回的值是上次设备重启的Unix time

这里要注意:这个API返回的值也会受系统时间影响,用户如果修改时间,值也会随着变化。

参考文章

1. iOS关于时间的处理

后记

谢谢这个技术大牛的资料,关于时间以前并未碰到特别的需求也不会了解那么多,现在算是学到了不少了,未完,待续~~~

推荐阅读更多精彩内容

  • 转自:iOS关于时间的处理 做App避免不了要和时间打交道,关于时间的处理,里面有不少门道,远不是一行API调用,...
    咖啡绿茶1991阅读 246评论 0 0
  • 做App避免不了要和时间打交道,关于时间的处理,里面有不少门道,远不是一行API调用,获取当前系统时间这么简单。我...
    MrPeak阅读 3,140评论 0 41
  • 前言 在iOS开发过程中,经常会和时间打交道。例如用户在一个页面停留的时间、两个方法哪个执行更快等等。之前对于这部...
    WQ_UESTC阅读 2,273评论 0 11
  • 时间的概念 时间是线性的 均匀的, 同一时刻, 只有一个绝对只有一个时间值存在, 而时区的划分 只是为了方便而已 ...
    zh_19阅读 325评论 0 1
  • 在iOS开发中,经常会遇到各种各样的时间问题,8小时时差,时间戳,求时间间隔,农历等等。解决办法网上比比皆是,但大...
    真巧了_嘿阅读 1,687评论 0 7