先说个概念: Coordinated Universal Time(UTC):又称为世界标准时间; 比如,中国内地的时间与UTC的时差为+8,也就是UTC+8。 Calendar Time:日历时间,是用“从一个标准时间点到此时的时间经过的秒数”来表示的时间。日历时间对于不同的编译器,是不同的。 但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。
例如,time_t就是一种日历时间,其值表示从UTC(Coordinated Universal Time)时间1970年1月1日00:00:00(称为UNIX系统的Epoch时间) 到当前时刻的秒数。
这里面涉及了四个类型 : *1.FILETIME:结构持有的64位无符号的文件的日期和时间值。此值表示自UTC(Coordinated Universal Time)时间1601年1月1日开始的100纳秒为单位的时间。 *2.SYSTEMTIME:这个和tm有点类似,代表系统时间。其中,SYSTEMTIME.year的值为多少就表示公元哪一年。 *3.tm:Systemtime-1900得到tm.year,其他的还有些差别 tm.year的值加上1900才表示公元哪一年。 *4.time_t:是日历时间;其值表示从UTC(Coordinated Universal Time)时间1970年1月1日00:00:00(称为UNIX系统的Epoch时间) 到当前时刻的秒数。
FileTime转换为Time_t :
//#### FileTimeToTime_t #### void FileTimeToTime_t( FILETIME ft, time_t *t ) { LONGLONG ll; ULARGE_INTEGER ui; ui.LowPart = ft.dwLowDateTime; ui.HighPart = ft.dwHighDateTime; ll = ft.dwHighDateTime << 32 + ft.dwLowDateTime; //这一步是不是多余的 *t = ((LONGLONG)(ui.QuadPart - 116444736000000000) / 10000000);
//将ui.QuadPart 的数据类型ULONGLONG 强制转换为time_t 的 LONGLONG数据类型 }
//
struct {
DWORD LowPart;
DWORD HighPart;
};
和ULONGLONG QuadPart;组成共用体ULARGE_INTEGER
注释:公元1601年到公元1970年相差369年,转化为秒数即为11644473600 秒。
参见:
Windows API的时间结构体、时间转换及时间获取
如何在C++中将filetime时间转化为字符串?
//********************************************************************/
或是如下方法:
如果编译器中的long类型为4字节时,则 time_t(typedef time_t __int64) 和
filetime( filetime 结构体中有两个unsigned long类型的变量)占用字节大小一样(__int64 是有符号吧),则filetime先可以转化为time_t, 再由time_t转化为tm:
FILETIME是一个__int64。可以这样转化成__int64
filetime st;
__int64 d = * (__int64 * ) & st;
// 因为 filetime 结构体中的两个变量是unsigned long类型的 ,而 *(__int64 *)& st 只是表示将unsigned long类型 的变量st的数据按照__int64 (=longlong)来解释读取而已,并未实际将unsigned long类型 的变量st的数据 转换成__int64 (=longlong) 数据类型格式的。
__int64 d = *(__int64 *)& st;应该改为__int64 d =(__int64) st;不行,因为编译器一般只存在从基本数据 类型到基本数据 类型 的类型转换函数,而像从复合数据 类型到基本数据 类型 等的类型转换函数 一般不存在,即 编译器可能不存在从filetime 结构体 类型到__int64 的类型转换函数。
参见:
中的
为什么filetime 数据类型的变量st可以通过如下方式正确读取其所要表示的数值:
__int64 d = * (__int64 * ) & st?
//
or
//__int64 d2; //memcpy(
&
d2,
&
st,
sizeof
(d2)); (d - 116444736000000000) / 10000000
注释:
上一篇中介绍了C运行库中的时间处理函数。这一篇介绍Windows SDk中提供的时间函数。两种时间系统之间没有本质区别(事实上CRT时间是用Windows时间实现的,当然这是说的VC实现),同样提供本地时间和UTC时间之间的转换。不过,
CRT中的tm时间在SDK中对应为系统时间(SYSTEMTIME),CRT中的time_t时间在SDK中对应的为文件时间(FILETIME),
那个"特殊时刻"也变成1601年1月1日的子夜。
参见:
filetime 转化为 time_t 百度
filetime 时间单位 百度
time_t 时间单位 百度
附加:
补习一下时间单位(http://blog.vckbase.com/zaboli/archive/2005/06/29/8969.aspx )
1ms (毫秒) 1毫秒=0.001秒=10-3秒(millisecond) 1μs (微秒) 1微秒=0.000001=10-6秒(microsecond) 1ns (纳秒) 1纳秒=0.000000001秒=10-9秒(nanosecond ) 1ps (皮秒) 1皮秒=0.000000000001秒=10-12秒 1fs (飞秒) 1飞秒=0.00000000000001秒=10-15秒
FILETIME
1功能
FILETIME结构持有的64位无符号的文件的日期和时间值。此值表示自1601年1月1日开始的100纳秒为单位的时间。
2 语法
typedef struct _FILETIME {
DWORD dwLowDateTime; /* 低 32 位 */
DWORD dwHighDateTime; /* 高 32 位 */
} FILETIME, *PFILETIME, *LPFILETIME;
3 成员
dwLowDateTime
低位32位的文件的时间值。
dwHighDateTime
高位32位的文件的时间值。
4 要求
FILETIME是一个__int64。可以这样转化成__int64
__int64 d
=
*
(__int64
*
)
&
st;
//
or
__int64 d2; memcpy(
&
d2,
&
st,
sizeof
(d2));
转化为__int64是一个以100纳秒为单位的值
参考:
FILETIME, SYSTEMTIME 与 time_t 相互转换
//************************************************************ //FILETIME, SYSTEMTIME 与 time_t 相互转换
//#####SYSTEMTIME 与 FILETIME相互转换##### //可以使用系统函数 //FileTimeToSystemTime(&ftcreate,&stcreate);
//参数: //(lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) //说明 //根据一个FILETIME结构的内容,装载一个SYSTEMTIME结构 //返回值 //Long,非零表示成功,零表示失败。会设置GetLastError //参数表 //参数 类型及说明 //lpFileTime FILETIME,包含了文件时间的一个结构 //lpSystemTime SYSTEMTIME,用于装载系统时间信息的一个结构
//#####SYSTEMTIME 与 time_t相互转换#####
//#### Time_tToSystemTime #### void TimetToSystemTime( time_t t, LPSYSTEMTIME pst ) { FILETIME ft; LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000; ft.dwLowDateTime = (DWORD) ll; ft.dwHighDateTime = (DWORD)(ll >> 32); FileTimeToSystemTime( &ft, pst ); }
//#### SystemTimeToTime_t #### void SystemTimeToTime_t( SYSTEMTIME st, time_t *pt ) { FILETIME ft; SystemTimeToFileTime( &st, &ft ); LONGLONG ll; ULARGE_INTEGER ui; ui.LowPart = ft.dwLowDateTime; ui.HighPart = ft.dwHighDateTime; ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime; *pt = (DWORD)((LONGLONG)(ui.QuadPart - 116444736000000000) / 10000000); }
//#### FileTimeToTime_t #### void FileTimeToTime_t( FILETIME ft, time_t *t ) { LONGLONG ll; ULARGE_INTEGER ui; ui.LowPart = ft.dwLowDateTime; ui.HighPart = ft.dwHighDateTime; ll = ft.dwHighDateTime << 32 + ft.dwLowDateTime; //这一步是不是多余的 *t = ((LONGLONG)(ui.QuadPart - 116444736000000000) / 10000000);
//将ui.QuadPart 的数据类型ULONGLONG 强制转换为time_t 的 LONGLONG数据类型
}
//
struct {
DWORD LowPart;
DWORD HighPart;
};
和ULONGLONG QuadPart;组成共用体ULARGE_INTEGER
//********************************************************************/
参见:
这一篇介绍Windows SDk中提供的时间函数。两种时间系统之间没有本质区别(事实上CRT时间是用Windows时间实现的,当然这是说的VC实现),同样提供本地时间和UTC时间之间的转换。不过CRT中的tm时间在SDK中对应为系统时间(SYSTEMTIME),CRT中的time_t时间在SDK中对应的为文件时间(FILETIME),那个"特殊时刻"也变成1601年1月1日的子夜。
当然,首先要弄清楚FILETIME与SYSTEMTIME定义。
typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, *PFILETIME; typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME;
比较一下,很明显,FILETIME与time_t类似,是64位整型,不过FILETIME是以100纳秒(ns)为单位。SYSTEMTIME与tm类似,不过多了一项wMilliseconds。可以看出,SDK时间比CRT的时间提供了更高的精度。同时SDK提供了更丰富的函数来处理时间。
void GetSystemTime( LPSYSTEMTIME lpSystemTime); void GetLocalTime( LPSYSTEMTIME lpSystemTime);
这两个函数获得SYSTEMTIME形式的当前时间,不过GetSystemTime函数获得当前的UTC时间,GetLocalTime获得当前的本地时间,可以想象,获得的两个时间存在着时差。类似于CRT中提供tm与time_t之间的转换,SDK也提供了两个函数来转换SYSTEMTIME时间与FILETIME时间。
BOOL SystemTimeToFileTime( const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime); BOOL FileTimeToSystemTime( const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime);
函数命名很self-explanatory,就不用多说了吧。
SDK还提供了两个很有趣的函数。
BOOL LocalFileTimeToFileTime( const FILETIME* lpLocalFileTime, LPFILETIME lpFileTime); BOOL FileTimeToLocalFileTime( const FILETIME* lpFileTime, LPFILETIME lpLocalFileTime);
LocalFileTimeToFileTime函数将本地的FILETIME时间转换为对应的UTC的FILETIME时间。我觉得,这个函数只是通过将本地时间减去与UTC时间的时间差来实现转换,比如在东八区的本地时间转换为对应的UTC时间,只需要将本地时间减去8*60*60*1000*1000*10(单位100ns)。类似,FileTimeToLocalFileTime函数是将UTC时间转换为本地时间,它只是将减去时间差换成加上时间差。
了解了这些功能,让我们用代码说话吧。
#include <stdlib.h> #include <stdio.h> #include <time.h> #include <windows.h> int main() { SYSTEMTIME stLocal, stUTC, stUTC2; FILETIME ftLocal, ftUTC, ft; ULARGE_INTEGER uli; GetLocalTime(&stLocal); GetSystemTime(&stUTC); printf("Local System Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n" , stLocal.wYear, stLocal.wMonth, stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond); printf("UTC System Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n" , stUTC.wYear, stUTC.wMonth, stUTC.wDay, stUTC.wHour, stUTC.wMinute, stUTC.wSecond); SystemTimeToFileTime(&stLocal, &ftLocal); uli.LowPart = ftLocal.dwLowDateTime; uli.HighPart = ftLocal.dwHighDateTime; printf("Local File Time: %llu/n" , uli.QuadPart); LocalFileTimeToFileTime(&ftLocal, &ftUTC); uli.LowPart = ftUTC.dwLowDateTime; uli.HighPart = ftUTC.dwHighDateTime; printf("UTC File Time: %llu/n" , uli.QuadPart); FileTimeToSystemTime(&ftUTC, &stUTC2); printf("UTC System Time2 (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n" , stUTC2.wYear, stUTC2.wMonth, stUTC2.wDay, stUTC2.wHour, stUTC2.wMinute, stUTC2.wSecond); return EXIT_SUCCESS; }
程序输出结果如下:
代码13行GetLocalTime函数获得当前的本地SYSTEMTIME时间,14行获得对应的UTC的SYSTEMTIME时间,如输出结果前两行所显示,两者相差8小时(凌晨还在写博客,表扬下自己。。。)。
20行SystemTimeToFileTime函数将本地SYSTEMTIME时间转换为方便计算的本地FILETIME形式时间,如输出结果第三行所显示。
25行LocalFileTimeToFileTime函数将本地FileTime时间转换为对应的UTC的FILETIME时间,如输出结果第四行所显示。就像前面介绍的,如果你将输出结果第三,四两行所显示的数字相减,并除以10*1000*1000*60*60,你将会得出8,你可以算下试试,记住FILETIME是以100纳秒为单位的。
最后30行FileTimeToSystemTime将FILETIME时间转换为SYSTEMTIME时间。可以看出输出结果中第五行与第二行相同,这是必须的,因为两者都是当前本地时间对应的UTC时间。
分类: C/C++ General Programming
2010-02-26 22:03
4147人阅读
收藏
举报
时间处理时实际项目中经常碰到的问题,这里介绍最常用的时间处理函数。
首先介绍基本的时间概念。时间一般分为两种,一种是本地时间(Local Time),一种是协调世界时间(Coordinated Universal Time ,UTC),也就是传说中的格林威治时间。本地时间与UTC时间之间的差即为时差,比如,北京时间(东八区)比UTC时间晚8个小时。
C运行库中处理时间的函数主要是这四个:
time_t time( time_t *timer);
time_t类型为32位或64位整型,具体类型由编译系统决定。此函数用来获得从1970年1月1日子夜(这个时刻在不同的CRT实现中可能会不一样)到当前时刻以来所流逝的时间,以秒为单位。这个时间差叫做日历时间(Calendar Time )。
这是当然让我困惑的地方:这个特殊的时刻——1970年1月1日零时零分零秒——是指本地时间呢,还是UTC时间呢?我认为是本地时间,也就是各个时区自己的1970年1月1日零时零分零秒。可以设想这样一种情况,如果全球24时区各有一台电脑,都依次在自己所在时区的本地时间1970年1月1日零时1分零秒调用time函数,那么返回值都是60。注意,这里是依次调用(事实上是每隔1小时),而不是想象中的同时调用,这是因为相邻时区的同一本地时间,总是相差1小时。
当然,time_t型的时间方便计算机处理,但普通用户无法理解这种数字。所以我们通常需要将time_t型时间转换成我们平常所见的年月日形式。CRT中为此定义了tm结构。
struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; };
注释中已详细解释了各个字段的用法。显然这个结构中的字段对用户更有意义。我们通常用localtime_s函数将time_t时间转换为tm时间。
errno_t localtime_s( struct tm * _tm, const time_t *time);
其中第二个参数为传入的time_t时间,第一个参数为返回的tm时间。由函数名可看出,返回的tm时间表示的是本地时间。当然,我们有时候也需要获得对应的UTC时间,这时我们需要gmtime函数。
errno_t gmtime_s( struct tm * _tm, const time_t * time);
后面我们会看到两者的区别。
我们知道了如何将time_t时间转换为tm时间。同样,我们会需要将tm表示的时间转换为time_t时间。这时我们需要mktime函数。
time_t mktime( struct tm *timeptr);
此函数返回从"特殊时刻"到参数表示的时刻之间流逝的日历时间。另外还有个很好用的特性,就是它能修正传进来的tm结构中各字段的取值范围。比如,如果你将tm.tm_mon设为1,tm.tm_day设为33,然后以其为参数调用mktime函数,此函数会将tm.tm_mon修正为2,tm.tm_day修正为2。具体用法参照MSDN。
我们来分析下面示例代码:
#include <stdlib.h> #include <stdio.h> #include <time.h> int main() { struct tm tmLocal, tmUTC; time_t tNow; time(&tNow); printf("Time Now from time(): %llu/n" , tNow); localtime_s(&tmLocal, &tNow); printf("Local Time(YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n" , tmLocal.tm_year + 1900, tmLocal.tm_mon, tmLocal.tm_mday, tmLocal.tm_hour, tmLocal.tm_min, tmLocal.tm_sec); gmtime_s(&tmUTC, &tNow); printf("UTC Time (YYYY-MM-DD HH:MM:SS): %d-%d-%d %d:%d:%d/n" , tmUTC.tm_year + 1900, tmUTC.tm_mon, tmUTC.tm_mday, tmUTC.tm_hour, tmUTC.tm_min, tmUTC.tm_sec); tNow = mktime(&tmLocal); printf("Time Now from mktime(): %llu/n" , tNow); return EXIT_SUCCESS; }
输出结果如下:
上面代码中,11行time函数获得从"特殊时刻"到当前时刻的日历时间,如输出结果中的第一行显示的1267192581秒。
14行localtime_s函数将日历时间转换为本地tm时间,如输出结果第二行。
18行gmtime_s函数将将日历时间转换为对应的UTC的tm时间,如输出结果第三行显示。很容易看出,第二,三行输出的时间相差8小时,因为我在东八区。如果你修改自己电脑的时区(在控制面板的Date and Time中修改),再运行此程序,比较两次的运行结果,你就可以更好的理解了。
22行mktime函数将tm时间转换为日历时间,输出结果中第四行显示的结果与第一行一样,这是必须的。。。
TimeConvert.h
#if !defined(AFX_TIMECONVERT_H__87F35906_DBD6_46F1_BF78_2FA34FCB8409__INCLUDED_) #define AFX_TIMECONVERT_H__87F35906_DBD6_46F1_BF78_2FA34FCB8409__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <time.h> #include<olectl.h.> class TimeConvert { public : static void SystemTimeToFileTime( const SYSTEMTIME*pst,FILETIME *pft); static void Time_tToFileName( time_t t,FILETIME *ft); static void TmToTime_t( tm &_t, time_t &t); static void Time_tToTm( time_t t, tm *_t); static void TmToSystemTime( const tm &t,SYSTEMTIME *st); static void SystemTimeTOTm( const SYSTEMTIME &st, tm *t); static void FileTimeToTime_t( const FILETIME *ft, time_t *t); static void Time_tToSystemTime( time_t t, SYSTEMTIME *pst); static void FileTimeToSystemTime( const FILETIME *lpFileTime,SYSTEMTIME *lpSystemTime); static void SystemTimeToTime_T(SYSTEMTIME *st, time_t *pt ); static void FileTimeToTm( const FILETIME *ft, tm *t); static void TmToFileTime( tm &t,FILETIME *ft); static void TimeFormat( char *dest, int length, const char *format, const tm *t); private : TimeConvert(); virtual ~TimeConvert(); }; #endif // !defined(AFX_TIMECONVERT_H__87F35906_DBD6_46F1_BF78_2FA34FCB8409__INCLUDED_)
TimeConvert.cpp
#include "TimeConvert.h" TimeConvert::TimeConvert() { } TimeConvert::~TimeConvert() { } void TimeConvert::FileTimeToSystemTime( const FILETIME *lpFileTime,SYSTEMTIME *lpSystemTime) { ::FileTimeToSystemTime(lpFileTime,lpSystemTime); } void TimeConvert::Time_tToSystemTime( time_t t, SYSTEMTIME *pst ) { tm *m=localtime(&t); TmToSystemTime(*m,pst); } void TimeConvert::SystemTimeToTime_T(SYSTEMTIME *st, time_t *pt ) { FILETIME ft; SystemTimeToFileTime( st, &ft); FileTimeToTime_t(&ft,pt); } void TimeConvert::FileTimeToTime_t( const FILETIME *ft, time_t *t) { LONGLONG ll; ULARGE_INTEGER ui; ui.LowPart=ft->dwLowDateTime; ui.HighPart=ft->dwHighDateTime; ll=(ft->dwHighDateTime<<32)+ft->dwLowDateTime; *t=(DWORD )(( LONGLONG )(ui.QuadPart- 116444736000000000) /10000000); } void TimeConvert::SystemTimeTOTm( const SYSTEMTIME &st, tm *t) { tm gm= {st.wSecond, st.wMinute, st.wHour, st.wDay, st.wMonth-1, st.wYear-1900, st.wDayOfWeek, 0, 0}; memcpy(t,&gm,sizeof ( tm )); } void TimeConvert::TmToSystemTime( const tm &t,SYSTEMTIME *st) { SYSTEMTIME s={1900 + t.tm_year,1 + t.tm_mon,t.tm_wday,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec,0}; memcpy(st,&s,sizeof (SYSTEMTIME)); } void TimeConvert::Time_tToTm( time_t t, tm *_t) { tm *tt=localtime(&t); memcpy(_t,tt,sizeof ( tm )); } void TimeConvert::TmToTime_t( tm &_t, time_t &t) { t=mktime(&_t); } void TimeConvert::Time_tToFileName( time_t t,FILETIME *ft) { LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000; ft->dwLowDateTime = (DWORD ) ll; ft->dwHighDateTime = (DWORD )(ll >> 32); } void TimeConvert::FileTimeToTm( const FILETIME *ft, tm *t) { time_t tm ; FileTimeToTime_t(ft,&tm ); Time_tToTm(tm ,t); } void TimeConvert::TmToFileTime( tm &t,FILETIME *ft) { time_t tm ; TmToTime_t(t,tm ); Time_tToFileName(tm ,ft); } void TimeConvert::SystemTimeToFileTime( const SYSTEMTIME*pst,FILETIME *pft ) { ::SystemTimeToFileTime(pst,pft); } void TimeConvert::TimeFormat( char *dest, int length, const char *format, const tm *t) { strftime(dest,length,format,t); }