1. System Time
系统时间其实指的是当前UTC时间(世界标准时间),可以用GetSystemTime(&st)和SetSystemTime(&st)来获取和设置系统时间。调用SetSystemTime函数需要SE_SYSTEMTIME_NAME权限,所以运行时要以管理员的身份运行。怎么在代码中获取管理员权限这里就不展开了,可以查看msdn:http://msdn.microsoft.com/en-us/library/windows/desktop/ms717802(v=vs.85).aspx
SystemTime结构体定义如下:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
需要注意的就是一周从星期天开始,以0为基数算;一年是从一月份开始,以1为基数算。
可以用SetSystemTimeAdjustment函数使windows内部系统时钟与另一个时钟源同步(下面两个函数的时间单位都是100纳秒)
BOOL WINAPI SetSystemTimeAdjustment(
_In_ DWORD dwTimeAdjustment,
_In_ BOOL bTimeAdjustmentDisabled
);
相对应的有GetSystemTimeAdjustment函数
BOOL WINAPI GetSystemTimeAdjustment(
_Out_ PDWORD lpTimeAdjustment,
_Out_ PDWORD lpTimeIncrement,
_Out_ PBOOL lpTimeAdjustmentDisabled
);
其中,lpTimeIncrement的值自系统启动以来就是固定的,而且不会因为其他系统操作而改变。
当 bTimeAdjustmentDisabled为TRUE时,程序将会忽略 dwTimeAdjustment的值,系统将会使用内部的时钟同步机制来同步时钟;
当bTimeAdjustmentDisabled为FALSE时,系统将会重置lpTimeIncrement的值为dwTimeAdjustment,即当dwTimeAdjustment与原来的lpTimeIncrement的值一致时,系统时钟将会维持原来的正常速度;如果dwTimeAdjustment比lpTimeIncrement的值大一点,系统时钟就会比正常速度快一点,反之,如果小一点,那就慢一点。
2. Local Time
即你所在时区的时间。类似地,有GetLocalTime(&st)和SetLocalTime(&st)函数,st仍然是一个SYSTEMTIME结构体。需要注意的是,SetLocalTime也会改变系统时间(在改变系统时间之前会先转换成系统时间),而且也需要SE_SYSTEMTIME_NAME权限
获取当前时区信息用的是 GetTimeZoneInformation(&tzi),相应的有SetTimeZoneInformation(&tzi)函数(需要SE_TIME_ZONE_NAME权限)
DWORD WINAPI GetTimeZoneInformation(
_Out_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation
);
这个函数有4种返回代码/值:
TIME_ZONE_ID_UNKNOWN/0:当前时区没有使用夏令时,可能因为夏令时起止时间没有指定或者自动调整夏令时时钟被禁用了
TIME_ZONE_ID_STANDARD/1:当前时区使用标准时间
TIME_ZONE_ID_DAYLIGHT/2: 当前时区使用夏令时
如果因为其他原因而调用失败则会返回TIME_ZONE_ID_INVALID,详细情况用GetLastError查询。
tzi表示一个TIME_ZONE_INFORMATION结构体:
typedef struct _TIME_ZONE_INFORMATION {
LONG Bias;//UTC与当前时区时间的差值,以分钟为单位,即UTC time = local time + Bias
WCHAR StandardName[32];//标准时间的名称,注意是宽字符串
SYSTEMTIME StandardDate;//从夏令时转换到标准时间的日期与时间,其实也就是一年中夏令时的结束时间
LONG StandardBias;// UTC time = Standard time + Bias + StandardBias
WCHAR DaylightName[32];//夏令时的名称,注意也是宽字符串
SYSTEMTIME DaylightDate;//从标准时间转换到夏令时的日期与时间,其实也就是一年中夏令时的开始时间
LONG DaylightBias;// UTC time = Daylight time + Bias + DaylightBias
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION;
成员StandardDate和DaylightDate的情况比较复杂:
如果StandardDate的值被指定了,那么DaylightDate的值也必须被指定,否则系统将认为这个结构体的值是无效的;
如果当前时区不支持夏令时或者想要禁用夏令时,那么SYSTEMTIME结构体的成员wMonth的值必须为0;
在指定时间的时候,如果wYear的值不为0,那么所指定的时间是绝对时间;否则,为相对时间:当wYear为0时,wDay的值指的是wMonth月的第wDay个wDayOfWeek。比如2014年巴黎的夏令时的开始时间是3月30号2点,结束时间是10月26号3点,那么TIME_ZONE_INFORMATION结构体的值应该是这样:
此外,针对那些每年夏令时起止时间不一样的情况,windows提供了SetDynamicTimeZoneInformation(&dtzi)和GetDynamicTimeZoneInformation(&dtzi)函数。这两个函数与上面两个函数类似,除了参数是指向一个DYNAMIC_TIME_ZONE_INFORMATION的结构体:
typedef struct _TIME_DYNAMIC_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[32];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[32];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
WCHAR TimeZoneKeyName[128];//本地计算机注册的时区键名
BOOLEAN DynamicDaylightTimeDisabled;//是否禁用动态夏令时
} DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION;
与前面的TIME_ZONE_INFORMATION结构体相比,多了两个成员,其他成员的用法一样
最后,windows还提供了一个函数,让用户可以查询特定年份特定时区的信息:
BOOL WINAPI GetTimeZoneInformationForYear(
_In_ USHORT wYear,
_In_opt_ PDYNAMIC_TIME_ZONE_INFORMATION pdtzi,//如果为NULL,则使用当前时区信息
_Out_ LPTIME_ZONE_INFORMATION ptzi
);
在msdn上有一个例子,顺便也贴在这里吧
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <strsafe.h>
int main()
{
TIME_ZONE_INFORMATION tziOld, tziNew, tziTest;
DWORD dwRet;
// Enable the required privilege
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_TIME_ZONE_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
// Retrieve the current time zone information
dwRet = GetTimeZoneInformation(&tziOld);
if(dwRet == TIME_ZONE_ID_STANDARD || dwRet == TIME_ZONE_ID_UNKNOWN)
wprintf(L"%s\n", tziOld.StandardName);
else if( dwRet == TIME_ZONE_ID_DAYLIGHT )
wprintf(L"%s\n", tziOld.DaylightName);
else
{
printf("GTZI failed (%d)\n", GetLastError());
return 0;
}
// Adjust the time zone information
ZeroMemory(&tziNew, sizeof(tziNew));
tziNew.Bias = tziOld.Bias + 60;
StringCchCopy(tziNew.StandardName, 32, L"Test Standard Zone");
tziNew.StandardDate.wMonth = 10;
tziNew.StandardDate.wDayOfWeek = 0;
tziNew.StandardDate.wDay = 5;
tziNew.StandardDate.wHour = 2;
StringCchCopy(tziNew.DaylightName, 32, L"Test Daylight Zone");
tziNew.DaylightDate.wMonth = 4;
tziNew.DaylightDate.wDayOfWeek = 0;
tziNew.DaylightDate.wDay = 1;
tziNew.DaylightDate.wHour = 2;
tziNew.DaylightBias = -60;
if( !SetTimeZoneInformation( &tziNew ) )
{
printf("STZI failed (%d)\n", GetLastError());
return 0;
}
// Retrieve and display the newly set time zone information
dwRet = GetTimeZoneInformation(&tziTest);
if(dwRet == TIME_ZONE_ID_STANDARD || dwRet == TIME_ZONE_ID_UNKNOWN)
wprintf(L"%s\n", tziTest.StandardName);
else if( dwRet == TIME_ZONE_ID_DAYLIGHT )
wprintf(L"%s\n", tziTest.DaylightName);
else printf("GTZI failed (%d)\n", GetLastError());
// Disable the privilege
tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
return 1;
}