版本記錄
| 版本號(hào) | 時(shí)間 |
|---|---|
| V1.0 | 2017.08.30 |
前言
做
app一定會(huì)和時(shí)間打交道,關(guān)于時(shí)間的處理有很多值得我們研究和注意的地方,時(shí)間是線性的,地球上各個(gè)時(shí)區(qū)的時(shí)間表示不一樣,但是它們的絕對(duì)時(shí)間值是一樣的,只是處于同一個(gè)時(shí)空我們對(duì)同一時(shí)間的表述或者理解不同罷了,這幾篇就說(shuō)一下關(guān)于時(shí)間的問(wèn)題。
兩種時(shí)間參考
1. GMT
人類(lèi)對(duì)于時(shí)間的理解還很有限,但我們至少能確定一點(diǎn):時(shí)間的變化是勻速的。時(shí)間前進(jìn)的速度是均勻的,不會(huì)忽快忽慢,所以為了描述時(shí)間,我們也需要找到一個(gè)值,它的變化也是以均勻的速度向前變化的。
時(shí)間是線性勻速的,長(zhǎng)久以來(lái)人們也是在找時(shí)間的參考物,前人發(fā)現(xiàn)抬頭看太陽(yáng)是個(gè)好辦法,太陽(yáng)總是按規(guī)律的“早起晚落”,而且“亙古不變”,可以用太陽(yáng)在一天當(dāng)中所處的位置來(lái)描述當(dāng)前的時(shí)間。后來(lái)不同地區(qū)的文化需要交流,你這里太陽(yáng)正高空照,我這可能已經(jīng)下山了,所以需要有一個(gè)公共的大家都認(rèn)可的地方,以這個(gè)地方太陽(yáng)的位置來(lái)做參考著,溝通起來(lái)就會(huì)方便很多。最后選擇的是英國(guó)倫敦的格林尼治天文臺(tái)所在地,以格林尼治的時(shí)間作為公共時(shí)間,也就是我們所說(shuō)的GMT時(shí)間(Greenwich Mean Time)。
2. UTC
太陽(yáng)所處的位置變化跟地球的自轉(zhuǎn)相關(guān),過(guò)去人們認(rèn)為地球自轉(zhuǎn)的速率是恒定的,但在1960年這一認(rèn)知被推翻了,人們發(fā)現(xiàn)地球自轉(zhuǎn)的速率正變得越來(lái)越慢,而時(shí)間前進(jìn)的速率還是恒定的,所以GMT不再被認(rèn)為可以用來(lái)精準(zhǔn)的描述時(shí)間了。
我們需要繼續(xù)尋找一個(gè)勻速前進(jìn)的值。抬頭看天是我們從宏觀方向去尋找答案,科技的發(fā)展讓我們?cè)谖⒂^方面取得了更深的認(rèn)識(shí),于是有聰明人根據(jù)微觀粒子原子的物理屬性,建立了原子鐘,以這種原子鐘來(lái)衡量時(shí)間的變化,原子鐘50億年才會(huì)誤差1秒,這種精讀已經(jīng)遠(yuǎn)勝于GMT了。這個(gè)原子鐘所反映的時(shí)間,也就是我們現(xiàn)在所使用的UTC(Coordinated Universal Time )標(biāo)準(zhǔn)時(shí)間。
幾種獲取時(shí)間的方式
1. NSDate
先看一下它的開(kāi)發(fā)文檔。
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)
上面那一段翻譯過(guò)來(lái)就是:NSDate對(duì)象封裝單個(gè)時(shí)間點(diǎn),與任何特定的日歷系統(tǒng)或時(shí)區(qū)無(wú)關(guān)。 日期對(duì)象是不可變的,表示相對(duì)于絕對(duì)參考日期(2001年1月1日00:00:00 UTC)的不變時(shí)間間隔,它是以UTC為標(biāo)準(zhǔn)的。
下面看一下代碼
- (void)demoTimeIntervalSinceReferenceDate
{
NSDate *date = [NSDate date];
NSLog(@"date = %lf", date.timeIntervalSinceReferenceDate);
}
下面看輸出結(jié)果
2017-08-30 15:41:13.225921+0800 JJOC[1294:766001] date = 525771673.225875
下面我們計(jì)算一下: 525771673.225875/365/86400 = 16.6721104,今年是2017年,距離2001年正好是16年。
我們?cè)倏聪旅孢@個(gè)例子
- (void)demoDate
{
NSDate *date = [NSDate date];
NSLog(@"date = %@", date);
}
下面看輸出結(jié)果
2017-08-30 15:44:54.197111+0800 JJOC[1301:767209] date = Wed Aug 30 15:44:54 2017
可見(jiàn)我這里輸出的就是我當(dāng)前的時(shí)間。
這里還要注意:NSDate是受手機(jī)系統(tǒng)時(shí)間控制的。也就是說(shuō),當(dāng)你修改了手機(jī)上的時(shí)間顯示,NSDate獲取當(dāng)前時(shí)間的輸出也會(huì)隨之改變。在我們做App的時(shí)候,明白這一點(diǎn),就知道NSDate并不可靠,因?yàn)橛脩?hù)可能會(huì)修改它的值。
2. 函數(shù)CFAbsoluteTimeGetCurrent()
該函數(shù)出自Core Foundation框架,我們先看一下官方文檔中的說(shuō)明。
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.
這段文字翻譯過(guò)來(lái)就是:相對(duì)于2001年1月1日00:00:00 GMT的絕對(duì)參考日期,絕對(duì)時(shí)間以秒為單位。 正值表示參考日期后的日期,負(fù)值表示其前的日期。 例如,絕對(duì)時(shí)間-32940326相當(dāng)于1999年12月16日17:54:34。 對(duì)此功能的重復(fù)調(diào)用不能保證單調(diào)增加的結(jié)果。 由于與外部時(shí)間參考的同步或由于顯式的用戶(hù)更改時(shí)鐘,系統(tǒng)時(shí)間可能會(huì)減少。
這里還要注意:CFAbsoluteTimeGetCurrent()也會(huì)跟著當(dāng)前設(shè)備的系統(tǒng)時(shí)間一起變化,也可能會(huì)被用戶(hù)修改。
3. 函數(shù)gettimeofday
還是先看一下API
int gettimeofday(struct timeval * __restrict, void * __restrict);
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
NSLog(@"gettimeofday: %ld", now.tv_sec);
該函數(shù)獲取的時(shí)間是Unix time。
Unix time是以UTC 1970年1月1號(hào) 00:00:00為基準(zhǔn)時(shí)間,當(dāng)前時(shí)間距離基準(zhǔn)點(diǎn)偏移的秒數(shù)。
其實(shí)NSDate中也提供了獲取這個(gè)時(shí)間的接口
- (void)demoTimeIntervalSince1970
{
NSDate *date = [NSDate date];
NSLog(@"date = %f", date.timeIntervalSince1970);
}
下面看輸出結(jié)果
2017-08-30 16:02:54.282284+0800 JJOC[1307:770247] date = 1504080174.282267
同樣,下面我們計(jì)算一下: 1504080174.282267/365/86400 = 47.694069444444445,今年是2017年,距離1970年正好是47年。
這里還要注意:gettimeofday和NSDate,CFAbsoluteTimeGetCurrent()一樣,都是受當(dāng)前設(shè)備的系統(tǒng)時(shí)間影響。只不過(guò)是參考的時(shí)間基準(zhǔn)點(diǎn)不一樣而已。我們和服務(wù)器通訊的時(shí)候一般使用Unix time。
4. mach_absolute_time()
我們需要找到一個(gè)均勻變化的屬性值來(lái)描述時(shí)間,而在我們的iPhone上剛好有一個(gè)這樣的值存在,就是CPU的時(shí)鐘周期數(shù)(ticks)。這個(gè)tick的數(shù)值可以用來(lái)描述時(shí)間,而mach_absolute_time()返回的就是CPU已經(jīng)運(yùn)行的tick的數(shù)量。將這個(gè)tick數(shù)經(jīng)過(guò)一定的轉(zhuǎn)換就可以變成秒數(shù),或者納秒數(shù),這樣就和時(shí)間直接關(guān)聯(lián)了。
不過(guò)這個(gè)tick數(shù),在每次手機(jī)重啟之后,會(huì)重新開(kāi)始計(jì)數(shù),而且iPhone鎖屏進(jìn)入休眠之后tick也會(huì)暫停計(jì)數(shù)。
這里要注意:mach_absolute_time()不會(huì)受系統(tǒng)時(shí)間影響,只受設(shè)備重啟和休眠行為影響。
5. CACurrentMediaTime()
該函數(shù)是在QuartzCore框架中CABase.h中的函數(shù)。
/* 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數(shù)轉(zhuǎn)化成秒數(shù)的結(jié)果。
下面看示例代碼
- (void)demoCACurrentMediaTime
{
double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);
}
下面看輸出結(jié)果
2017-08-30 16:20:02.937803+0800 JJOC[1311:772292] CACurrentMediaTime: 194746.231819
這里輸出的就是開(kāi)機(jī)后設(shè)備一共運(yùn)行了(設(shè)備休眠不統(tǒng)計(jì)在內(nèi))多少秒,另一個(gè)API也能返回相同的值。
- (void)demoSystemUptime
{
NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);
}
看輸出結(jié)果
2017-08-30 16:22:42.853656+0800 JJOC[1314:772878] systemUptime: 194906.147670
這里要注意:CACurrentMediaTime()也不會(huì)受系統(tǒng)時(shí)間影響,只受設(shè)備重啟和休眠行為影響。
6. sysctl
該函數(shù)來(lái)自于下面的文件。

下面看代碼
#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);
}
}
下面看輸出結(jié)果
2017-08-30 16:29:49.043262+0800 JJOC[1318:773969] time = 1503235239
返回的值是上次設(shè)備重啟的Unix time。
這里要注意:這個(gè)API返回的值也會(huì)受系統(tǒng)時(shí)間影響,用戶(hù)如果修改時(shí)間,值也會(huì)隨著變化。
參考文章
后記
謝謝這個(gè)技術(shù)大牛的資料,關(guān)于時(shí)間以前并未碰到特別的需求也不會(huì)了解那么多,現(xiàn)在算是學(xué)到了不少了,未完,待續(xù)~~~
