溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C語言Unix時間戳與本地時間的轉(zhuǎn)化

發(fā)布時間:2021-03-04 15:37:46 來源:億速云 閱讀:180 作者:TREX 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“C語言Unix時間戳與本地時間的轉(zhuǎn)化”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“C語言Unix時間戳與本地時間的轉(zhuǎn)化”吧!

前言

我們平常說時間都說的幾點幾分幾秒,星期幾,但是在計算機里面并不是直接使用我們所說的時間,而是使用Unix時間戳,這樣不管是哪個平臺,哪個系統(tǒng),都可以根據(jù)自己對時間的定義進行轉(zhuǎn)換,像Java,PHP等都提供了接口來進行轉(zhuǎn)化,C庫里面也有這樣的函數(shù),那具體是怎么實現(xiàn)的呢?要了解這個問題首先我們就必須要清楚什么是Unix時間戳,什么是我們平常使用的時間。

1. Unix時間戳

UNIX時間戳:Unix時間戳(英文為Unix epoch, Unix time, POSIX time 或 Unix timestamp)

是從1970年1月1日(UTC/GMT的午夜)開始所經(jīng)過的秒數(shù),不考慮閏秒。

UNIX時間戳的0按照ISO 8601規(guī)范為 :1970-01-01T00:00:00Z.

一個小時表示為UNIX時間戳格式為:3600秒;一天表示為UNIX時間戳為86400秒,閏秒不計算。

在大多數(shù)的UNIX系統(tǒng)中UNIX時間戳存儲為32位,這樣會引發(fā)2038年問題或Y2038。

對應(yīng)的時間和秒數(shù)如下

時間
1 分鐘60 秒
1 小時3600 秒
1 天86400 秒
1 周604800 秒
1 月 (30.44 天)2629743 秒
1 年 (365.24 天)31556926 秒

2. 本地時間

平常我們最常聽見的是陰歷和陽歷之分,陰歷是按照月亮的運行規(guī)律來計算日期的,那我們所說的陽歷又是什么呢?其實陽歷或者說叫西歷最早是叫做儒略歷,是由羅馬共和國獨裁官儒略·愷撒(即蓋烏斯·尤里烏斯·凱撒)采納數(shù)學(xué)家兼天文學(xué)家索西琴尼的計算后,于公元前45年1月1日起執(zhí)行的取代舊羅馬歷法的一種歷法,但令人遺憾的是,當(dāng)時那些頒發(fā)歷書的祭司們,卻不了解改歷的實質(zhì)。結(jié)果,可笑的是,當(dāng)時羅馬執(zhí)掌頒布歷書的祭司竟把原來歷法上規(guī)定的“每隔三年置閏”誤解為“每三年置一閏”。從公元前45年起,到公元前9年為止,這之間本應(yīng)設(shè)置10個閏年,他們卻設(shè)置了13個閏年。公元前9年,人們終于發(fā)現(xiàn)這一差錯,這時愷撒的外甥奧古斯都執(zhí)掌政權(quán),他糾正了這個錯誤,才停止了“三年一閏”。奧古斯都下令改正過來,改到次年(公元前8年)才置閏年。當(dāng)改正這種閏年的錯誤時已經(jīng)多閏了3年,為了去掉著多閏的3年,奧古斯都又下令停閏3年,即以公元前5年、公元前1年、公元前4年仍為平年,以后恢復(fù)了每4年一閏的規(guī)定了。奧古斯都為了宣揚這一功勞,仿效儒略·愷撒的做法,下令把自己出生的儒略歷中的8月改稱為奧古斯都月(這一名稱在西方沿用到今天)。8月后的大,小月份都翻轉(zhuǎn)過來了,9月為30天,10月為31天,11月為30天,12月為31天,這種置月方式一直沿用至今。如此一來,一年多出了一天,于是也從二月份29天里再減去一天,二月份只剩下28天了 1582年羅馬教皇格里高利對”儒略歷”又進行修改,規(guī)定被4整除的年為閏年,但逢百之年只有能被400除盡才能是閏年。這就是使用至今的“格里歷”。這樣做是為了使歷年與回歸年相接近?;貧w年的周期是365.2425天。儒略歷一年的平均長度為365.25日,比回歸年(365.2425天)長11分14秒,自公元325年(該年采用儒略歷作為宗教日歷)積累到十六世紀(jì)末,春分日由3月21日提早到3月11日。于是羅馬教皇格里高利十三世(Gregorius XⅢ)于1582年10月4日還下令將次日(即原10月5日)定為10月15日,把春分日又恢復(fù)為3月21日。這樣,1582年的10月5日-14日這十天就成了“不存在”的日子,變?yōu)闅v史的空白。1949年9月27日,經(jīng)過中國人民政治協(xié)商會議第一屆全體會議通過,中華人民共和國使用國際社會多數(shù)國家通用的西歷和西元作為歷法和紀(jì)年。

3. 蔡勒公式

清楚了Unix時間戳和公歷的意義,怎么講秒數(shù)轉(zhuǎn)化成本地時間大家心里應(yīng)該有個大概了,既然本地時間知道了,那我們可不可以根據(jù)這個日期直接知道那一天是星期幾呢?數(shù)學(xué)家蔡勒(Zeller)推算出了這個公式,大家稱為蔡勒公式,使用這個公式隨便給出一個日期,就可以計算出是星期幾。

公式具體是這樣的

W = [C / 4] - 2C + y + [y / 4] + [13 * (M + 1) / 5] + d - 1

或者是:w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1

公式中的符號含義如下:

w:星期; w對7取模得:0-星期日,1-星期一,2-星期二,3-星期三,4-星期四,5-星期五,6-星期六

c:世紀(jì)-1(前兩位數(shù))

y:年(后兩位數(shù))

m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月來計算,比如2003年1月1日要看作2002年的13月1日來計算)

d:日 [ ]代表取整,即只要整數(shù)部分。

下面以中華人民共和國成立100周年紀(jì)念日那天(2049年10月1日)來計算是星期幾,過程如下:

w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1

= 49 + [49 / 4] + [20 / 4] - 2 × 20 + [26 × (10 + 1) / 10] + 1 - 1

= 49 + [12.25] + 5 - 40 + [28.6]

= 49 + 12 + 5 - 40 + 28

= 54 (除以7余5)

即2049年10月1日(100周年國慶)是星期五。

再比如計算2006年4月4日,過程如下:

w = y + [y / 4] + [c / 4] - 2c + [26(m + 1) / 10] + d - 1

= 6 + [6 / 4] + [20 / 4] - 2 * 20 + [26 * (4 + 1) / 10] + 4 - 1

= -12 (除以7余5,注意對負數(shù)的取模運算!實際上應(yīng)該是星期二而不是星期五)

不過要注意的是,蔡勒公式只適合于1582年(明朝萬歷十年)10月15日之后的情形。

4. 具體實現(xiàn)

清楚了上面這些內(nèi)容,我們來看一下用C怎么實現(xiàn)Unix時間戳和本地時間的相互轉(zhuǎn)化,并根據(jù)指定的時間算出對應(yīng)的日期

#include <stdio.h>
#include <stdbool.h>

#define UTC_BASE_YEAR 1970
#define MONTH_PER_YEAR 12
#define DAY_PER_YEAR 365
#define SEC_PER_DAY 86400
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60

/* 每個月的天數(shù) */
const unsigned char g_day_per_mon[MONTH_PER_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* 自定義的時間結(jié)構(gòu)體 */
typedef struct
{
 unsigned short nYear;
 unsigned char nMonth;
 unsigned char nDay;
 unsigned char nHour;
 unsigned char nMin;
 unsigned char nSec;
 unsigned char DayIndex; /* 0 = Sunday */
} mytime_struct;

/*
 * 功能:
 * 判斷是否是閏年
 * 參數(shù):
 * year:需要判斷的年份數(shù)
 *
 * 返回值:
 * 閏年返回1,否則返回0
 */
unsigned char applib_dt_is_leap_year(unsigned short year)
{
 /*----------------------------------------------------------------*/
 /* Local Variables      */
 /*----------------------------------------------------------------*/

 /*----------------------------------------------------------------*/
 /* Code Body       */
 /*----------------------------------------------------------------*/
 if ((year % 400) == 0) {
 return 1;
 } else if ((year % 100) == 0) {
 return 0;
 } else if ((year % 4) == 0) {
 return 1;
 } else {
 return 0;
 }
}

/*
 * 功能:
 * 得到每個月有多少天
 * 參數(shù):
 * month:需要得到天數(shù)的月份數(shù)
 * year:該月所對應(yīng)的年份數(shù)
 *
 * 返回值:
 * 該月有多少天
 *
 */
unsigned char applib_dt_last_day_of_mon(unsigned char month, unsigned short year)
{
 /*----------------------------------------------------------------*/
 /* Local Variables      */
 /*----------------------------------------------------------------*/

 /*----------------------------------------------------------------*/
 /* Code Body       */
 /*----------------------------------------------------------------*/
 if ((month == 0) || (month > 12)) {
 return g_day_per_mon[1] + applib_dt_is_leap_year(year);
 }

 if (month != 2) {
 return g_day_per_mon[month - 1];
 } else {
 return g_day_per_mon[1] + applib_dt_is_leap_year(year);
 }
}

/*
 * 功能:
 * 根據(jù)給定的日期得到對應(yīng)的星期
 * 參數(shù):
 * year:給定的年份
 * month:給定的月份
 * day:給定的天數(shù)
 *
 * 返回值:
 * 對應(yīng)的星期數(shù),0 - 星期天 ... 6 - 星期六
 */
unsigned char applib_dt_dayindex(unsigned short year, unsigned char month, unsigned char day)
{
 char century_code, year_code, month_code, day_code;
 int week = 0;

 century_code = year_code = month_code = day_code = 0;

 if (month == 1 || month == 2) {
 century_code = (year - 1) / 100;
 year_code = (year - 1) % 100;
 month_code = month + 12;
 day_code = day;
 } else {
 century_code = year / 100;
 year_code = year % 100;
 month_code = month;
 day_code = day;
 }

 /* 根據(jù)蔡勒公式計算星期 */
 week = year_code + year_code / 4 + century_code / 4 - 2 * century_code + 26 * ( month_code + 1 ) / 10 + day_code - 1;
 week = week > 0 ? (week % 7) : ((week % 7) + 7);

 return week;
}

/*
 * 功能:
 * 根據(jù)UTC時間戳得到對應(yīng)的日期
 * 參數(shù):
 * utc_sec:給定的UTC時間戳
 * result:計算出的結(jié)果
 * daylightSaving:是否是夏令時
 *
 * 返回值:
 * 無
 */
void utc_sec_2_mytime(unsigned int utc_sec, mytime_struct *result, bool daylightSaving)
{
 /*----------------------------------------------------------------*/
 /* Local Variables      */
 /*----------------------------------------------------------------*/
 int sec, day;
 unsigned short y;
 unsigned char m;
 unsigned short d;
 unsigned char dst;

 /*----------------------------------------------------------------*/
 /* Code Body       */
 /*----------------------------------------------------------------*/

 if (daylightSaving) {
 utc_sec += SEC_PER_HOUR;
 }

 /* hour, min, sec */
 /* hour */
 sec = utc_sec % SEC_PER_DAY;
 result->nHour = sec / SEC_PER_HOUR;

 /* min */
 sec %= SEC_PER_HOUR;
 result->nMin = sec / SEC_PER_MIN;

 /* sec */
 result->nSec = sec % SEC_PER_MIN;

 /* year, month, day */
 /* year */
 /* year */
 day = utc_sec / SEC_PER_DAY;
 for (y = UTC_BASE_YEAR; day > 0; y++) {
 d = (DAY_PER_YEAR + applib_dt_is_leap_year(y));
 if (day >= d)
 {
  day -= d;
 }
 else
 {
  break;
 }
 }

 result->nYear = y;

 for (m = 1; m < MONTH_PER_YEAR; m++) {
 d = applib_dt_last_day_of_mon(m, y);
 if (day >= d) {
  day -= d;
 } else {
  break;
 }
 }

 result->nMonth = m;
 result->nDay = (unsigned char) (day + 1);
 /* 根據(jù)給定的日期得到對應(yīng)的星期 */
 result->DayIndex = applib_dt_dayindex(result->nYear, result->nMonth, result->nDay);
}

/*
 * 功能:
 * 根據(jù)時間計算出UTC時間戳
 * 參數(shù):
 * currTime:給定的時間
 * daylightSaving:是否是夏令時
 *
 * 返回值:
 * UTC時間戳
 */
unsigned int mytime_2_utc_sec(mytime_struct *currTime, bool daylightSaving)
{
 /*----------------------------------------------------------------*/
 /* Local Variables      */
 /*----------------------------------------------------------------*/
 unsigned short i;
 unsigned int no_of_days = 0;
 int utc_time;
 unsigned char dst;

 /*----------------------------------------------------------------*/
 /* Code Body       */
 /*----------------------------------------------------------------*/
 if (currTime->nYear < UTC_BASE_YEAR) {
 return 0;
 }

 /* year */
 for (i = UTC_BASE_YEAR; i < currTime->nYear; i++) {
 no_of_days += (DAY_PER_YEAR + applib_dt_is_leap_year(i));
 }

 /* month */
 for (i = 1; i < currTime->nMonth; i++) {
 no_of_days += applib_dt_last_day_of_mon((unsigned char) i, currTime->nYear);
 }

 /* day */
 no_of_days += (currTime->nDay - 1);

 /* sec */
 utc_time = (unsigned int) no_of_days * SEC_PER_DAY + (unsigned int) (currTime->nHour * SEC_PER_HOUR +
        currTime->nMin * SEC_PER_MIN + currTime->nSec);

 if (dst && daylightSaving) {
 utc_time -= SEC_PER_HOUR;
 }

 return utc_time;
}

int main(int argc, char *argv[])
{
 mytime_struct my_time;
 unsigned int sec;
 char *DayIndex[] = {"Sun.", "Mon.", "Tues.", "Wed.", "Thur.", "Fri.", "Sat."};

 /* 這里根據(jù)UTC時間戳計算出來的時間是零時區(qū)的時間,所以如果要轉(zhuǎn)化成北京時間就需要多加8小時 */
 utc_sec_2_mytime(1484537668 + 8 * SEC_PER_HOUR, &my_time, false);


 printf("%d-%d-%d %d:%d:%d %s\n", my_time.nYear, my_time.nMonth, my_time.nDay,
  my_time.nHour, my_time.nMin, my_time.nSec, DayIndex[my_time.DayIndex]);

 sec = mytime_2_utc_sec(&my_time, false);
 printf("sec = %d\n", sec);

 return 0;
}

基本上的內(nèi)容就這些了,平常最好能夠直接使用系統(tǒng)提供的接口,如果不能使用的話就自己實現(xiàn),根據(jù)上面的代碼修改成自己需要的,如果有不對的地方,希望能夠批評指正

總結(jié)

到此,相信大家對“C語言Unix時間戳與本地時間的轉(zhuǎn)化”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI