1. 程式人生 > >C實現Unix時間戳和本地時間轉化

C實現Unix時間戳和本地時間轉化

我們平常說時間都說的幾點幾分幾秒,星期幾,但是在計算機裡面並不是直接使用我們所說的時間,而是使用Unix時間戳,這樣不管是哪個平臺,哪個系統,都可以根據自己對時間的定義進行轉換,像Java,PHP等都提供了介面來進行轉化,C庫裡面也有這樣的函式,那具體是怎麼實現的呢?要了解這個問題首先我們就必須要清楚什麼是Unix時間戳,什麼是我們平常使用的時間。

1. Unix時間戳

UNIX時間戳:Unix時間戳(英文為Unix epoch, Unix time, POSIX time 或 Unix timestamp)
是從1970年1月1日(UTC/GMT的午夜)開始所經過的秒數,不考慮閏秒。
UNIX時間戳的0按照ISO 8601規範為 :1970-01-01T00:00:00Z.
一個小時表示為UNIX時間戳格式為:3600秒;一天表示為UNIX時間戳為86400秒,閏秒不計算。
在大多數的UNIX系統中UNIX時間戳儲存為32位,這樣會引發2038年問題或Y2038。

對應的時間和秒數如下

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

2. 本地時間

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

3. 蔡勒公式

清楚了Unix時間戳和公曆的意義,怎麼講秒數轉化成本地時間大家心裡應該有個大概了,既然本地時間知道了,那我們可不可以根據這個日期直接知道那一天是星期幾呢?數學家蔡勒(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

:世紀-1(前兩位數)
y:年(後兩位數)
m:月(m大於等於3,小於等於14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月來計算,比如2003年1月1日要看作2002年的13月1日來計算)
d:日 [ ]代表取整,即只要整數部分。

下面以中華人民共和國成立100週年紀念日那天(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,注意對負數的取模運算!實際上應該是星期二而不是星期五)

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

4. 具體實現

清楚了上面這些內容,我們來看一下用C怎麼實現Unix時間戳和本地時間的相互轉化,並根據指定的時間算出對應的日期

#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

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

/* 自定義的時間結構體 */
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;

/*
 * 功能:
 *     判斷是否是閏年
 * 引數:
 *     year:需要判斷的年份數
 *
 * 返回值:
 *     閏年返回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;
    }
}

/*
 * 功能:
 *     得到每個月有多少天
 * 引數:
 *     month:需要得到天數的月份數
 *     year:該月所對應的年份數
 *
 * 返回值:
 *     該月有多少天
 *
 */
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);
    }
}

/*
 * 功能:
 *     根據給定的日期得到對應的星期
 * 引數:
 *     year:給定的年份
 *     month:給定的月份
 *     day:給定的天數
 *
 * 返回值:
 *     對應的星期數,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;
    }

    /* 根據蔡勒公式計算星期 */
    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;
}

/*
 * 功能:
 *     根據UTC時間戳得到對應的日期
 * 引數:
 *     utc_sec:給定的UTC時間戳
 *     result:計算出的結果
 *     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);
    /* 根據給定的日期得到對應的星期 */
    result->DayIndex = applib_dt_dayindex(result->nYear, result->nMonth, result->nDay);
}

/*
 * 功能:
 *     根據時間計算出UTC時間戳
 * 引數:
 *     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."};

    /* 這裡根據UTC時間戳計算出來的時間是零時區的時間,所以如果要轉化成北京時間就需要多加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;
}

基本上的內容就這些了,平常最好能夠直接使用系統提供的介面,如果不能使用的話就自己實現,根據上面的程式碼修改成自己需要的,如果有不對的地方,希望能夠批評指正

相關推薦

C實現Unix時間本地時間轉化

我們平常說時間都說的幾點幾分幾秒,星期幾,但是在計算機裡面並不是直接使用我們所說的時間,而是使用Unix時間戳,這樣不管是哪個平臺,哪個系統,都可以根據自己對時間的定義進行轉換,像Java,PHP等都提供了介面來進行轉化,C庫裡面也有這樣的函式,那具體是怎麼實現

unix時間普通時間 轉換

unix時間戳是從1970年1月1日(UTC/GMT的午夜)開始所經過的秒數,不考慮閏秒,以秒為單位 new Date().getTime()獲得的是以毫秒為單位的 js中獲取unix時間戳的方式

Unix時間北京時間相互轉換

場景需求:介面上有時間顯示,不管是服務端還是app端需要將Unix時間戳轉換成北京時間 坑:網上基本上都是這樣的解法 坑:很容易讓人疑惑我全出來的123456789是int型別的,其實這個123456789是long型別的。 具體Code程式碼如下:

(Lua) 客戶端本地時間伺服器時間不一致的解決方案

問題來源:伺服器的時間是固定的,國內一般都設定為北京時間(東8時區的時間),而客戶端分佈在世界各地,客戶端系統設定的時區是不固定的。很多時候在設計時,沒有考慮時區不一致的情況,直接使用時間戳來進行時間轉換和比較,往往出現很多預想不到的問題。基本概念/方法:1、【時區】:全世界

C++ 時間本地時間 函式

時間戳轉本地時間在網上找了一上午,中午研究出來一個,同時進行了一些改進。 .h檔案 //時間戳轉本地時間。returnType=0,返回年月日時分秒,returnType=1,返回年月日,ret

php獲取今日開始時間結束時間

mktime date 開始時間 begin 時間 ide nth 時間戳 oda 1、php獲取今日開始時間戳和結束時間戳 $beginToday=mktime(0,0,0,date(‘m‘),date(‘d‘),date(‘Y‘));$endToday=mktime(

php 獲取今日、昨日、上周、本月的起始時間結束時間的方法

.cn str OS pad yesterday lock cells highlight tle php 獲取今日、昨日、上周、本月的起始時間戳和結束時間戳的方法,主要使用到了 php 的時間函數 mktime。下面首先還是直奔主題以示例說明如何使用 mktime 獲取

java 時間PHP時間 的轉換 php time()

time 時間 去掉 ech 導致 format ava 轉換問題 scu 最近在弄discuz,數據庫mysql,時間類型int 10 總結一下java 時間戳和PHP時間戳 的轉換問題: 由於精度不同,導致長度不一致,直接轉換錯誤。 JAVA時間戳長度是13位,如:12

PHP的strtotime()函式轉換的時間實際時間不一致解決方案

做一個時間區間搜尋的功能時,遇到一個問題,使用strtotime()轉換時間去比較時,發現搜尋不到,前端傳遞的是   但是PHP轉換後時間戳為1540732715,相當於   與實際時間相差8小時,所以搜尋不到的原因就在此。解決問題很簡單。 修改P

MYSQL時間一般時間格式相互轉化

原文地址:【http://www.2cto.com/database/201208/145029.html】 1、UNIX_TIMESTAMP(date) UNIX_TIMESTAMP(date)   如果沒有引數呼叫,返回一個Unix時間戳記(從'1970-01-01 00:00

php算每月天數方法,十二個月每月開始時間結束時間方法

/*算天數*/ function is_yue_tian_num($nian,$month){ if (in_array($month, array(1, 3, 5, 7, 8, 01, 03, 05, 07, 08, 10, 12))) { $text =

php 獲取今日、昨日、上週、本月的起始時間結束時間的方法

//php獲取今日開始時間戳和結束時間戳 $beginToday=mktime(0,0,0,date('m'),date('d'),date('Y')); $endToday=mktime(0,0,0,date('m'),date('d')+1,date('Y'))-1;//

Linux時間標準時間的互轉

在LINUX系統中,有許多場合都使用時間戳的方式表示時間,即從1970年1月1日起至當前的天數或秒數。如/etc/shadow裡的密碼更改日期和失效日期,還有代理伺服器的訪問日誌對訪問時間的記錄等等。下面介紹幾種時間戳格式和標準時間格式轉換的方法:1、分別以標準格式和時間戳來顯示當前時間[[email

Python 時間本地時間處理

Python 時間獲取 要使用到python time模組 程式碼如下: import time print time.time() 結果: 1472483797.276373 結果為浮點型的 時間戳 指格林威治時間1970年01

iOS 時間標準時間的轉換

#pragma mark--時間戳轉保準時間 - (NSString *)timerWith:(NSString *)time { NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

關於laravel5.2從資料庫取出的時間本地時間不相同

  larave5.2從資料庫取出來的時間戳是已經格式化了的,但是我發現從取出的時間戳與本地時間不相同,於是一通找,發現把 config/app.php 裡的 'timezone' => 'UTC',修改為 PRC 或 Asia/Shanghai 問題即可解決  PS

java 時間PHP時間 的轉換[10位13位]

總結一下java 時間戳和PHP時間戳 的轉換問題:  由於精度不同,導致長度不一致,直接轉換錯誤。  JAVA時間戳長度是13位,如:1294890876859  PHP時間戳長度是10位, 如:1294890859  主要最後三位的不同,JAVA時間戳在PHP中使用,

Linux時間標準時間的互轉-zz

在LINUX系統中,有許多場合都使用時間戳的方式表示時間,即從1970年1月1日起至當前的天數或秒數。如/etc/shadow裡的密碼更改日期和失效日期,還有代理伺服器的訪問日誌對訪問時間的記錄等等。 下面介紹幾種時間戳格式和標準時間格式轉換的方法: 1、分別以標準格式和時間戳來顯示當前時間 [[emai

常用時間處理方法:時間格式化時間之間轉換;時間比大小

1、獲取當前格式化時間: // 獲取當前時間的時間戳,並轉換成格式化時間 long getNowTimeLong = System.currentTimeMillis(); //轉換成12小時進位制 SimpleDateFormat fromatTi

關於laravel從資料庫取出的時間本地時間不相同

 larave從資料庫取出來的時間戳是已經格式化了的,但是我發現從取出的時間戳與本地時間不相同,於是一通找,發現把 config/app.php 裡的 'timezone' => 'UTC',修改為 PRC 或 Asia/Shanghai 問題即可解決 PS:UTC世界