首页 > 代码库 > 获取网络时间

获取网络时间

这两天有一个应用需要获取网络时间,虽然一直知道可以从时间服务器获取时间,却从来也没有操作过,借这个机会重新进行一下深入了了解。

基本的思路就是:通过SOCKET连接时间服务器,直接接收从服务器发送的过来的时间数据。

void GetNetTime()
{
    TIME_ZONE_INFORMATION tzinfo;
    DWORD dwStandardDaylight;
    int nRet;

    /* Get server IP */
    struct hostent *host;
    char *pServerIP;

    host = gethostbyname("time.nist.gov");
    if (NULL == host)
    {
        return;
    }

    pServerIP = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);

    /* Connect to time server, and get time */
	WORD wVersionRequested;
	WSADATA wsaData;
	int nErrCode;
    SOCKET sockfd;

	wVersionRequested = MAKEWORD(2, 2);
	nErrCode = WSAStartup(wVersionRequested, &wsaData);
    if (0 != nErrCode)
    {
        return;
    }

    char cTimeBuf[40] = { 0 };
    unsigned long ulTime = 0;

    do 
    {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (INVALID_SOCKET == sockfd)
        {
            continue;
        }

        int TimeOut = 3000;//设置接收超时6秒
        if(::setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut,sizeof(TimeOut)) == SOCKET_ERROR)
        {
            return;
        }

	    sockaddr_in	addr;

	    memset(&addr, 0, sizeof(addr));
	    addr.sin_family         = AF_INET;
	    addr.sin_port           = htons(37);
	    addr.sin_addr.s_addr    = inet_addr(pServerIP);

        nRet = connect(sockfd, (sockaddr *)&addr, sizeof(addr));
	    if (SOCKET_ERROR == nRet)
	    {
            continue;
	    }

        nRet = recv(sockfd, (char *)&ulTime, sizeof(ulTime), 0);
        if ((SOCKET_ERROR != nRet) && (0 != nRet))
        {
            break;
        }

        closesocket(sockfd);
    } while (1);

    closesocket(sockfd);

    /* 到时为止,获取到的只是网络序的4个字节时间数据,
       需要先转换成本地字节序,然后再通过ConvertTime函数转换成可以识别的字符串。
    */
    unsigned long ulTimehl = ntohl(ulTime);
    ConvertTime(ulTimehl);

    return;    
}


void ConvertTime(unsigned long ulTime)
{
    // Windows文件时间是一个64位的值,它是从1601年1月1日中午12:00到现在的时间间隔,
    // 单位是1/10,000,000秒,即1000万分之1秒(100-nanosecond)
    FILETIME ft;
    SYSTEMTIME st;

    // 首先将基准时间(1900年1月1日0点0分0秒0毫秒)转化为Windows文件时间
    st.wYear = 1900;
    st.wMonth = 1;
    st.wDay = 1;
    st.wHour = 0;
    st.wMinute = 0;
    st.wSecond = 0;
    st.wMilliseconds = 0;

    SystemTimeToFileTime(&st, &ft);

    // 然后将Time Protocol使用的基准时间加上逝去的时间(ulTime)
    LONGLONG *pLLong = (LONGLONG *)&ft;

    /* 注意:
       文件时间单位是1/1000 0000秒(即100ns),
       需要将从时间服务器上获取的以秒为单位的ulTime做一下转换
    */
    *pLLong += (LONGLONG) 10000000 * ulTime;

    // 再将时间转化回来,更新系统时间
    FileTimeToSystemTime(&ft, &st);

    TRACE(_T("%04d%02d%02d %02d:%02d:%02d\n"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond );

    return;
}

各地时差都不一致,可以根据GetTimeDiff函数计算当地时差,对上面的时间加以调整:

void GetTimeDiff()
{
    float bias;
    long sminute,shour;
 

    /* 获取时区信息 */
    dwStandardDaylight = GetTimeZoneInformation(&tzinfo); //获取时区与UTC的时间差 应该返回-8
    if (dwStandardDaylight == TIME_ZONE_ID_INVALID) //函数执行失败
    {
        return; 
    }

    /* 时差调整 */
    bias = tzinfo.Bias;
    if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) //标准时间有效
        bias += tzinfo.StandardBias;

    if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) //夏令时间
        bias += tzinfo.DaylightBias;

    shour   = bias / 60;
    sminute = fmod(bias, (float)60);
}



获取网络时间