深度揭秘(unix時(shí)間戳轉(zhuǎn)換成時(shí)間命令)unix時(shí)間戳轉(zhuǎn)換成時(shí)間指令,聊聊時(shí)間(2)硬核算法篇——UNIX時(shí)間戳轉(zhuǎn)UTC時(shí)間,巧連神數(shù),
目錄:
1.unix時(shí)間戳轉(zhuǎn)換成時(shí)間指令
2.unix時(shí)間戳轉(zhuǎn)換工具
3.unix時(shí)間轉(zhuǎn)換器
4.unix時(shí)間戳格式
5.unix時(shí)間換算
6.unix時(shí)間戳 時(shí)區(qū)
7.unix時(shí)間戳當(dāng)前時(shí)間
8.unix時(shí)間轉(zhuǎn)換為時(shí)間戳
9.unix時(shí)間戳轉(zhuǎn)換成時(shí)間公式
10.unix時(shí)間戳轉(zhuǎn)換公式
1.unix時(shí)間戳轉(zhuǎn)換成時(shí)間指令
文/Edward前一篇文章《聊聊時(shí)間(1)UNIX時(shí)間戳和UTC時(shí)間》中,我們講述了計(jì)算機(jī)中UNIX時(shí)間戳和UTC時(shí)間的基本概念,以及簡(jiǎn)要地闡述了諸如STM32F103之類芯片簡(jiǎn)單功能RTC的時(shí)間轉(zhuǎn)換問(wèn)題,這篇文章將會(huì)夾帶慢慢的干貨,詳細(xì)地討論UTC時(shí)間和Linux時(shí)間戳轉(zhuǎn)換的算法,這個(gè)經(jīng)典的算法必將愛(ài)戀下載使你有一天會(huì)受用。
2.unix時(shí)間戳轉(zhuǎn)換工具
1 32位的UNIX時(shí)間戳我們之前的文章有介紹過(guò)UNIX操作系統(tǒng)是伴隨著C語(yǔ)言一起誕生的,其誕生的時(shí)間大致是在20世紀(jì)70年代左右,當(dāng)時(shí)的計(jì)算機(jī)都是以8位,16位的數(shù)據(jù)寬度去處理數(shù)據(jù)的,因此在當(dāng)時(shí)那個(gè)年代,找一個(gè)32位的寄存器來(lái)存儲(chǔ)時(shí)間,是一件比較奢侈的事情。
3.unix時(shí)間轉(zhuǎn)換器
最早的UNIX時(shí)間戳就只是用一個(gè)32位的寄存器來(lái)存儲(chǔ)的而UNIX時(shí)間戳規(guī)定,UTC/GMT的1970年1月1日0時(shí)0分0秒為UNIX時(shí)間戳的起始計(jì)數(shù)時(shí)刻,即UNIX時(shí)間戳從這個(gè)時(shí)間點(diǎn)開始技術(shù)由于使用的變量是一個(gè)32位的二進(jìn)制數(shù),因此這個(gè)計(jì)數(shù)器的最大計(jì)數(shù)長(zhǎng)度為2^32=4,愛(ài)戀下載294,967,296s,所以在UNIX時(shí)間戳的誕生之初,它最大時(shí)間只能累積計(jì)數(shù)到2038年左右。
4.unix時(shí)間戳格式
但是,這是不是說(shuō)到了2038年這個(gè)計(jì)數(shù)器溢出之后會(huì)發(fā)生類似“千年蟲”之類的危機(jī)呢?這個(gè)問(wèn)題,大家無(wú)需杞人憂天,因?yàn)楝F(xiàn)在的桌面和移動(dòng)處理器早已經(jīng)跨入64位CPU時(shí)代了,這個(gè)UNIX時(shí)間戳計(jì)數(shù)器的長(zhǎng)度也早已經(jīng)被擴(kuò)充到64位。
5.unix時(shí)間換算
而我們今天使用32位的UNIX時(shí)間戳計(jì)數(shù)器,完全是為了類似STM32這種處理器來(lái)講述的
6.unix時(shí)間戳 時(shí)區(qū)
圖1 溢出 2 UNIX時(shí)間戳轉(zhuǎn)UTC時(shí)間2.1 時(shí)分秒的轉(zhuǎn)換前面我們已經(jīng)說(shuō)過(guò),UNIX時(shí)間戳是以UTC/GMT時(shí)間的1970愛(ài)戀下載年1月1日0時(shí)0分0秒為起始時(shí)刻進(jìn)行計(jì)數(shù)的考慮到這個(gè)UNIX時(shí)間戳的計(jì)數(shù)器的數(shù)據(jù)長(zhǎng)度為32位,我們不妨先將這個(gè)UNIX時(shí)間戳計(jì)數(shù)器定義為一個(gè)變量,其名稱為“u32UnixTimeStamp”。
7.unix時(shí)間戳當(dāng)前時(shí)間
首先,基于上述的闡述,我們思考下,當(dāng)這個(gè)1970年1月1日0時(shí)0分0秒時(shí)刻過(guò)了1s,那么“u32UnixTimeStamp”變量的值也會(huì)自加1,變成1隨著時(shí)間的流逝,“u32UnixTimeStamp”的值不斷地自加,當(dāng)“u32UnixTimeStamp”變成60的時(shí)候,我們回過(guò)頭來(lái)再看看UTC時(shí)間,由于此時(shí)已經(jīng)過(guò)了60s,因此UTC時(shí)間的秒數(shù)值將會(huì)溢出被清掉,分?jǐn)?shù)值將會(huì)被置1。愛(ài)戀下載
8.unix時(shí)間轉(zhuǎn)換為時(shí)間戳
這個(gè)時(shí)候UTC的時(shí)間變成了1970年1月1日0時(shí)1分0秒.再隨著時(shí)間的流逝,當(dāng)“u32UnixTimeStamp”的值變成3600的時(shí)候,此時(shí)1個(gè)小時(shí)已經(jīng)悄然流逝,這個(gè)時(shí)候UTC的時(shí)間變成了1970年1月1日1時(shí)0分0秒。
9.unix時(shí)間戳轉(zhuǎn)換成時(shí)間公式
86400秒流逝之后,“u32UnixTimeStamp”的值變成86400,此時(shí)正好24個(gè)小時(shí)過(guò)去,此時(shí)UTC時(shí)間變成了1970年1月2日0時(shí)0分0秒其實(shí),當(dāng)UNIX時(shí)間戳的計(jì)數(shù)值小于86400的時(shí)候,我們很容易就能寫出轉(zhuǎn)換成UTC時(shí)間的程序,因?yàn)樾r(shí)數(shù)就是“u32UnixTimeStamp”對(duì)3200取模,分?jǐn)?shù)就愛(ài)戀下載是將不能湊滿小時(shí)的“u32UnixTimeStamp”對(duì)60取模,剩余不能湊滿分?jǐn)?shù)的“u32UnixTimeStamp”即為當(dāng)前時(shí)間的秒數(shù),具體代碼如圖2所示。
10.unix時(shí)間戳轉(zhuǎn)換公式
圖2 計(jì)算一天之內(nèi)時(shí)分秒的代碼#include#include"timex_test1.h"utc_t UtcTime;intmain(void){int retVal;int u32UnixTimeStamp =
0;int hour, minute, sec;printf("input the UNIX time stamp:");scanf("%d", &u32UnixTimeStamp);if(u32U愛(ài)戀下載nixTimeStamp >=
86400) {printf("input out of range!\n"); retVal = 1; }else { hour = u32UnixTimeStamp / 3600
; //計(jì)算小時(shí) minute = (u32UnixTimeStamp - hour * 3600) / 60; //計(jì)算分 sec = (u32UnixTimeStamp - hour *
3600 - minute * 60); //計(jì)算秒 UtcTime.year = 1970; UtcTime.month = 1; UtcTime.date = 1; UtcTime.hour =愛(ài)戀下載
0 + hour; UtcTime.minute = 0 + minute; UtcTime.second = 0 + sec;printf("\nUTC time is : %dy - %dm - %dd\n"
, UtcTime.year, UtcTime.month, UtcTime.date);printf("\nUTC time is : %dh - %dm : %ds\n", UtcTime.hour, UtcTime.minute, UtcTime.second);
retVal = 0; }return retVal;}#ifndef __TIMEX_H_#define __TIM愛(ài)戀下載EX_H_/*定義UTC時(shí)間結(jié)構(gòu)體類型*/typedefstruct{int
year;int month;int date;int hour;int minute;int second;} utc_t;#endif?以上的代碼是計(jì)算“u32UnixTimeStamp”小于86400時(shí)當(dāng)前UTC時(shí)間的時(shí)分秒代碼。
其實(shí),細(xì)心的讀者可能會(huì)發(fā)現(xiàn),這個(gè)86400數(shù)值代表的即是一天之內(nèi)的UTC時(shí)分秒,那么如果當(dāng)前的“u32UnixTimeStamp”計(jì)數(shù)值大于86400,我們?cè)撛趺刺幚砟兀课覀兿葋?lái)從最簡(jiǎn)單的情況來(lái)推導(dǎo)如果一個(gè)“u32UnixTimeStamp”為86401,那么此時(shí)我們可以知道,這個(gè)UNI愛(ài)戀下載X時(shí)間戳其實(shí)就是86400 + 1,已知當(dāng)“u32UnixTimeStamp”為86400時(shí),UTC時(shí)間將會(huì)變成1970年1月2日0時(shí)0分0秒,那么此時(shí)86401即為1970年1月2日0時(shí)0分0秒 + 1秒,即1970年1月2日0時(shí)0分1秒。
由這個(gè)簡(jiǎn)單的推導(dǎo),我們又可以將大于86400的數(shù)值拉到0~86400的范圍之內(nèi)了,同時(shí),還可以得到一個(gè)和日期相關(guān)的“原子”單位,即“日”2.2 年月日的轉(zhuǎn)換接下來(lái),我們將要再對(duì)“日”的上一層單位進(jìn)行討論,即“月”數(shù)值,這也將是這個(gè)程序最為復(fù)雜的一部分內(nèi)容。
這個(gè)復(fù)雜點(diǎn)主要體現(xiàn)在兩個(gè)方面: (1)每個(gè)月的天數(shù)不等眾所周知,一年中每個(gè)月的天數(shù)都是不愛(ài)戀下載同的,1,3,5,7,8,10,12為大月,一個(gè)月有31天;4,6,9,11為小月,一個(gè)月有30天 (2)閏年平年的影響。
由于公歷的偏差,導(dǎo)致了一年中最為特殊的一個(gè)月份2月,當(dāng)此年為閏年時(shí),2月份有29天,此年為平年時(shí),2月份有28天上面兩個(gè)原因,導(dǎo)致了年月日計(jì)算的復(fù)雜性但是,困難只是表面上的,我們仔細(xì)思考下,就很容易得出規(guī)律。
這個(gè)規(guī)律的突破口即為閏年出現(xiàn)的時(shí)間,因?yàn)殚c年每四年出現(xiàn)一次,那么我們可以列出從1970年開始的幾個(gè)年份如圖3所示
圖3 1970年開始的年份閏年平年分布由于閏年每四年出現(xiàn)一次,因此我們由圖3中可以得出一個(gè)簡(jiǎn)單方法,即可以從1790年開始,每四年組成一個(gè)集合,愛(ài)戀下載每一個(gè)集合的都是由1年閏年加上3年平年組成的,它們的時(shí)間都是相等的,即126230400秒。
因此這個(gè)月數(shù)的求解步驟就可以變成: (1)計(jì)算從1970年開始到當(dāng)前的UNIX時(shí)間戳為止,一共過(guò)了多少個(gè)“集合年(平年+閏年)”; (2)計(jì)算出當(dāng)前的UNIX時(shí)間戳位于本“集合年“的哪一年,這樣就可以判斷當(dāng)年年份是平年還是閏年;
(3)判斷了當(dāng)前年份是平年還是閏年之后,就可以推算出2月份有多少天,然后可以根據(jù)上述的遞歸法,求解出當(dāng)前位于某一月,某一天。首先,我們先來(lái)寫代碼,求出當(dāng)前的年份。如圖4所示。
圖4 計(jì)算當(dāng)前年份代碼得出當(dāng)前年份之后,我們就可以很容易使用“能被4整除且不能愛(ài)戀下載被100整除,或者能被400整除的年份是閏年”這一條規(guī)則算出當(dāng)年年份是閏年還是平年接著,我們可以直接將當(dāng)前年份剩余的時(shí)間戳結(jié)合平年還是閏年,查表計(jì)算出當(dāng)前的月份。
最后代碼如圖5所示。
圖5 UNIX時(shí)間戳轉(zhuǎn)UTC時(shí)間代碼#include#include"timex_test2.h"utc_t UtcTime;intmain(void){int retVal;int u32UnixTimeStamp =
0;int hour, minute, sec;int flat_year_month_day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 3愛(ài)戀下載0,
31};int leap_year_month_day[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};int year_temp =
0;int day_temp = 0;unsignedint cnt_temp = 0;int is_leap_or_flat_year;int i;printf("input the UNIX time stamp:"
);scanf("%d", &u32UnixTimeStamp); cnt_temp = u32UnixTimeStamp;/*判斷當(dāng)前UNIX時(shí)間戳經(jīng)過(guò)了多少個(gè)4年*/wh愛(ài)戀下載ile(cnt_temp >=
126230400) { year_temp ++; cnt_temp = cnt_temp - 126230400; }/*計(jì)算出當(dāng)前的4年周期起始年份*/ UtcTime.year = UNIX_TIME_YEAR + (
4 * year_temp); /*計(jì)算出當(dāng)前的年份*//*這部分代碼可使用循環(huán)做精簡(jiǎn),為了直觀,我將其寫開*/if(cnt_temp >= 31536000) { UtcTime.year ++;
cnt_temp -= 31536000;/*Flat year*/if(cnt_temp >= 31536000) { UtcTime.year愛(ài)戀下載 ++; cnt_temp -=
31536000;/*Leap year*/if(cnt_temp >= 31622400) { UtcTime.year ++; cnt_temp -= 31622400
;/*Flat year*/if(cnt_temp >= 31536000) { UtcTime.year ++; cnt_temp -= 31536000
; } } } }/*計(jì)算當(dāng)前年份是平年還是閏年*/if((((UtcTime.year % 4) == 0) && ((UtcTime.year % 100) !=
0)) || ((UtcTime.year % 400) == 0)) {愛(ài)戀下載 is_leap_or_flat_year = LEAP_YEAR; }else { is_leap_or_flat_year = FLAT_YEAR;
} /*計(jì)算出不足一年剩余的天數(shù)*/ day_temp = cnt_temp / 86400;/*剩余不足86400s的時(shí)間戳,計(jì)算出時(shí)間*/ UtcTime.hour = (cnt_temp - day_temp *
86400) / 3600; //Calculate hours UtcTime.minute = (cnt_temp - day_temp * 86400 - UtcTime.hour *
3600) / 60; //Calcul愛(ài)戀下載ate minutes UtcTime.second = cnt_temp % 60; /*將天數(shù)結(jié)合平年還是閏年查表計(jì)算出當(dāng)前的月份*/ UtcTime.month =
1;for(i = 0; i = flat_year_month_day[i +
1]) { UtcTime.month ++; day_temp -= flat_year_month_day[i + 1]; } } elseif
(is_leap_or_flat_year == LEAP_YEAR) {if(day_temp >= leap_year_month_day[i + 1]) { UtcTime.month ++;
da愛(ài)戀下載y_temp -= leap_year_month_day[i + 1]; } } }/*由于天數(shù)從1開始,因此需要加1*/ UtcTime.date = day_temp +
1;printf("\nTime transform successfully\n");printf("++++++++++++++++++++++++++++++++++\n");printf("\nUTC time is : %dy - %dm - %dd\n"
, UtcTime.year, UtcTime.month, UtcTime.date);printf("\nUTC time is : %dh - %dm 愛(ài)戀下載: %ds\n", UtcTime.hour, UtcTime.minute, UtcTime.second);
printf("++++++++++++++++++++++++++++++++++\n");printf("\n");printf("\n");return0;}#ifndef __TIMEX_H_#
define __TIMEX_H_/*定義UTC時(shí)間結(jié)構(gòu)體類型*/typedefstruct{int year;int month;int date;int hour;int minute;int second;
} utc_t;/*定義UNIX時(shí)間戳的起始UNIX時(shí)間*/#define愛(ài)戀下載 UNIX_TIME_YEAR 1970#define UNIX_TIME_MONTH 1#define UNIX_TIME_DATE 1
#define UNIX_TIME_HOUR 0#define UNIX_TIME_MINIUTE 0#define UNIX_TIME_SECOND 0#define LEAP_YEAR 1
#define FLAT_YEAR 0#endif最后,我們可以使用在線工具來(lái)驗(yàn)證轉(zhuǎn)換結(jié)果是否正確,可以參考下列鏈接的UNIX時(shí)間戳在線轉(zhuǎn)換工具,鏈接:https://tool.lu/timestamp/
假設(shè)我們同時(shí)輸入U(xiǎn)NIX時(shí)間戳1615906780,可以得出轉(zhuǎn)換的愛(ài)戀下載北京時(shí)間為2021-03-16 22:59:40,而我們程序轉(zhuǎn)換得出的時(shí)間為2021-03-16 14:59:40,這兩個(gè)結(jié)果看似有出入,其實(shí)是一致的,我們知道北京時(shí)間是東8區(qū)的時(shí)間,而UTC時(shí)間是0時(shí)區(qū)的時(shí)間,因此相差8小時(shí)。
如圖6所示
圖6 轉(zhuǎn)換結(jié)果下一期,我們將會(huì)講述UTC時(shí)間如何轉(zhuǎn)換成UNIX時(shí)間戳,以及如何進(jìn)行時(shí)區(qū)轉(zhuǎn)換。