一寸光阴一寸金,寸金难买寸光阴。——唐·王贞白
逝者如斯夫,不舍昼夜。——孔子
1. 时间概念
在日常生活中我们遇到的和时间相关的概念有北京时间、时差、12小时制、24小时制等,在软件开发中我们也经常遇到和时间相关的概念,软件虽说是一个虚拟的事物,但它仍然是来源于生活,不会脱离生活。我们在开发中遇到的时间概念大多可以对应上日常生活中的时间概念,但也有一些是软件作者(如微软等)人为制造的一些概念,如系统时间、文件时间等。
-
格林威治时间(UTC Time)
本初子午线被定义为通过格林威治经线的位置,经过本初子午线的时间便被称为格林威治时间,相对这条经线的时区向东递增,向西递减,每隔一个时区,相差一个小时。 -
本地时间(LocalTime)
本地时间就是系统设置时区的当前时间,比如说当前系统设置的时区为“(UTC+08:00)北京,重庆,香港特别行政区,乌鲁木齐”(东八区),系统的右下角通知区域显示的时间为“2017/09/05 16:57”,那么这个时间就是当前系统的本地时间。 -
系统时间(SystemTime)
Windows的系统时间是就是格林威治时间(UTC时间)。 -
文件时间( FileTime )
Windows的文件时间为一个64位整数(用FILETIME
结构体存储),可以理解为时间戳,只是这个时间戳比较特别,它记录从1601-1-1 00:00:00
到当前格林威治时间(UTC)所经过的100纳秒(ns)数。注意,将这个数据转换为秒的话要除以10^7(1秒 = 10^9纳秒,这里是100纳秒单位)。 -
时间戳(Timestamp)
一个长整型整数,一般指从1970-01-01 00:00:00
到当前格林威治时间(UTC)所经过的秒数,也有的指所经过的毫秒数。可以从站长之家查看。
2. 如何获取时间
CRT(C RunTime Library)、Windows API、Linux API都提供了获取时间的函数,常用的和时间相关的API有:
time_t time(time_t * _Time); // 从1970-01-01 00:00:00到当前格林威治时间(UTC)所经过的秒数
errno_t gmtime_s(struct tm * _Tm, const time_t * _Time);
errno_t localtime_s(struct tm * _Tm, const time_t * _Time);
VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
VOID GetLocalTime(LPSYSTEMTIME lpSystemTime);
BOOL GetFileTime(HANDLE hFile,
LPFILETIME lpCreationTime,
LPFILETIME lpLastAccessTime,
LPFILETIME lpLastWriteTime);
BOOL LocalFileTimeToFileTime(CONST FILETIME *lpLocalFileTime, LPFILETIME lpFileTime);
VOID GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
BOOL SystemTimeToTzSpecificLocalTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation,
CONST SYSTEMTIME *lpUniversalTime,
LPSYSTEMTIME lpLocalTime);
BOOL TzSpecificLocalTimeToSystemTime(CONST TIME_ZONE_INFORMATION *lpTimeZoneInformation,
CONST SYSTEMTIME *lpLocalTime,
LPSYSTEMTIME lpUniversalTime);
BOOL FileTimeToSystemTime(CONST FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime);
Win32 API中常出现FileTime
,SystemTime
就是第1节中说到的文件时间、系统时间。理解了时间概念之后,大部分时间操作就可以通过上面的API完成,下面列举一些常用的时间获取和转换的方法。
2.1 获取当前格林威治时间(UTC)
CRT API 精确到秒
#include <time.h>
time_t tUTC;
time(&tUTC);
struct tm tmUTC;
gmtime_s(&tmUTC, &tUTC);
printf("当前UTC时间: %04d-%02d-%02d %02d:%02d:%02d",
tmUTC.tm_year + 1900,
tmUTC.tm_mon + 1,
tmUTC.tm_mday,
tmUTC.tm_hour,
tmUTC.tm_min,
tmUTC.tm_sec);
Win32 API 精确到毫秒
#include <windows.h>
SYSTEMTIME st;
GetSystemTime(&st);
printf("当前UTC时间: %04d-%02d-%02d %02d:%02d:%02d:%04d",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds);
2.2 获取当前本地时间
CRT API 精确到秒
time_t tUTC;
time(&tUTC);
struct tm tmLocal;
localtime_s(&tmLocal, &tUTC); // 根据系统时区设置来转换
printf("当前本地时间: %04d-%02d-%02d %02d:%02d:%02d",
tmLocal.tm_year + 1900,
tmLocal.tm_mon + 1,
tmLocal.tm_mday,
tmLocal.tm_hour,
tmLocal.tm_min,
tmLocal.tm_sec);
Win32 API 精确到毫秒
#include <windows.h>
SYSTEMTIME st;
GetLocalTime(&st);
printf("当前本地时间: %04d-%02d-%02d %02d:%02d:%02d:%04d",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds);
2.3 获取Windows某个文件的文件时间
Win32 API
#include <windows.h>
HANDLE hFile = CreateFile(TEXT("E:\test.txt"), GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
FILETIME ftLastWriteTime; // 以文件最后写入时间为例
BOOL bRet = GetFileTime(hFile, NULL, NULL, &ftLastWriteTime);
if(bRet) {
ULARGE_INTEGER uli;
uli.LowPart = ftLastWriteTime.dwLowDateTime;
uli.HighPart = ftLastWriteTime.dwHighDateTime;
printf("UTC文件上次写入时间戳: %llu
", uli.QuadPart);
SYSTEMTIME stUtcLastWrite;
FileTimeToSystemTime(&ftLastWriteTime, &stUtcLastWrite);
printf("UTC文件上次写入时间: %04d-%02d-%02d %02d:%02d:%02d:%04d
",
stUtcLastWrite.wYear, stUtcLastWrite.wMonth, stUtcLastWrite.wDay, stUtcLastWrite.wHour, stUtcLastWrite.wMinute, stUtcLastWrite.wSecond, stUtcLastWrite.wMilliseconds);
}
else {
printf("获取文件时间失败
");
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
2.4 获取时间戳(秒)
CRT API
#include <time.h>
time_t tUTC; // time_t为__int64类型
time(&tUTC); // tUTC中存储着从1970-01-01 00:00:00到当前格林威治时间(UTC)所经过的秒数
2.5 获取时间戳(微秒)
Win32 API
union {
long long ns100;
FILETIME ft;
} fileTime;
GetSystemTimeAsFileTime(&fileTime.ft);
// lNowMicroMS中存储着从1970-01-01 00:00:00到当前格林威治时间(UTC)所经过的微妙数
// 116444736000000000是从1601年1月1日00:00:00:000到1970年1月1日00:00:00:000所经过的100纳秒数
long long lNowMicroMS = (long long)((fileTime.ns100 - 116444736000000000LL) / 10LL);
Linux API
#include <sys/time.h>
struct timeval tv;
gettimeofday(&tv, NULL);
// lNowMicroMS中存储着从1970-01-01 00:00:00到当前格林威治时间(UTC)所经过的微妙数
//
long long lNowMicroMS = tv.tv_sec * 1000000 + tv.tv_usec;
3. 不同类型时间相互转化
3.1 UTC时间 转 本地时间
Win32 API
#include <windows.h>
SYSTEMTIME stUtc; // 构造UTC时间2017-09-05 15:24:55:120
stUtc.wYear = 2017;
stUtc.wMonth = 9;
stUtc.wDay = 5;
stUtc.wHour = 15;
stUtc.wMinute = 24;
stUtc.wSecond = 55;
stUtc.wMilliseconds = 120;
SYSTEMTIME stLocal;
SystemTimeToTzSpecificLocalTime(NULL, &stUtc, &stLocal);
printf("本地时间: %04d-%02d-%02d %02d:%02d:%02d:%04d",
stLocalLastWrite.wYear,
stLocalLastWrite.wMonth,
stLocalLastWrite.wDay,
stLocalLastWrite.wHour,
stLocalLastWrite.wMinute,
stLocalLastWrite.wSecond,
stLocalLastWrite.wMilliseconds);
3.2 本地时间 转 UTC时间
Win32 API
#include <windows.h>
SYSTEMTIME st; // 构造本地时间2017-09-05 23:24:55:120
st.wYear = 2017;
st.wMonth = 9;
st.wDay = 5;
st.wHour = 23;
st.wMinute = 24;
st.wSecond = 55;
st.wMilliseconds = 120;
SYSTEMTIME stUtc;
TzSpecificLocalTimeToSystemTime(NULL, &st, &stUtc);
printf("UTC时间: %04d-%02d-%02d %02d:%02d:%02d:%04d",
stUtc.wYear,
stUtc.wMonth,
stUtc.wDay,
stUtc.wHour,
stUtc.wMinute,
stUtc.wSecond,
stUtc.wMilliseconds);
3.3 时间戳(精确到秒) 转 UTC时间
CRT API
#include <time.h>
long timestamp = 1504622637;
struct tm tmUTC;
gmtime_s(&tmUTC, (time_t*)×tamp);
printf("UTC时间: %04d-%02d-%02d %02d:%02d:%02d",
tmUTC.tm_year + 1900,
tmUTC.tm_mon + 1,
tmUTC.tm_mday,
tmUTC.tm_hour,
tmUTC.tm_min,
tmUTC.tm_sec);
3.4 时间戳(精确到微妙)转 UTC时间
Win32 API
long long lTimestampMicroS = 1504677128644572; // 存储精确到微妙的时间戳
union {
long long ns100;
FILETIME ft;
} fileTime;
fileTime.ns100 = lTimestampMicroS * 10LL + 116444736000000000LL;
SYSTEMTIME utcTime;
FileTimeToSystemTime(&fileTime.ft, &utcTime);
printf("UTC时间: %04d-%02d-%02d %02d:%02d:%02d:%04d",
utcTime.wYear,
utcTime.wMonth,
utcTime.wDay,
utcTime.wHour,
utcTime.wMinute,
utcTime.wSecond,
utcTime.wMilliseconds);
3.5 UTC时间 转 时间戳(精确到秒)
CRT API
// 将UTC 2017年11月12日 13:14:15转成时间戳
//
struct tm tmUTC;
tmUTC.tm_year = 2017 - 1900; // 减去1900
tmUTC.tm_mon = 11 - 1; // 减去1
tmUTC.tm_mday = 12;
tmUTC.tm_hour = 13;
tmUTC.tm_min = 14;
tmUTC.tm_sec = 15;
time_t timastampSec = mktime(&tmUTC);