djd0
djd0

Reputation: 947

How can a Windows DATE be converted into a Unix time in c++

Windows uses the DATE type to represent dates. This is a double value representing the number of days since 30 December 1899, midnight.

How can a DATE be converted into a Unix timestamp (the number of seconds since 01 January 1970) value?

Specifically, how can this be achieved using only the c++ standard library, and windows libraries for which MinGW distributes header files?

For example, I can get a date property from an IShellFolder2:

void MyFunc(IShellFolder2 *folder, PCUITEMID_CHILD pidl, const SHCOLUMNID *columnid) {
    VARIANT* v = (VARIANT*) malloc(sizeof(VARIANT));
    DATE d;
    HRESULT hr = folder->GetDetailsEx(pidl, colid, v);
    if (SUCCEEDED(hr)) {
        hr = VariantChangeType(v, v, 0, VT_DATE);
        if (SUCCEEDED(hr)) {
             d = v->date;
        }
        VariantClear(v);
    }
    free(v);
    // process date here
}

How do I then transform this value for use with software that uses the Unix timestamp format?

Currently used header files (not all relevant to this specific issue):

#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <propkey.h>
#include <wchar.h>
#include <shlguid.h>
#include <string>
#include <vector>

Upvotes: 1

Views: 3015

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31639

Use VariantTimeToSystemTime to convert DATE to SYSTEMTIME.

Conversion of SYSTEMTIME to unix time is then a simple task.

In Visual Studio you could use COleDateTime but that's not available in MinGW

#include <Windows.h>
#include <stdio.h>
#include <time.h>
#include <OleAuto.h>

unsigned int unix_stamp_of_DATE(DATE date)
{
    //convert DATE to SYSTEMTIME
    SYSTEMTIME st;
    VariantTimeToSystemTime(date, &st);

    //convert SYSTEMTIME to FILETIME 
    FILETIME ft;
    SystemTimeToFileTime(&st, &ft);

    //convert FILETIME to ULARGE_INTEGER
    //then QuadPart is 64bit timestamp
    ULARGE_INTEGER ul{ ft.dwLowDateTime, ft.dwHighDateTime };
    return (unsigned int)((ul.QuadPart - 116444736000000000ULL)/10000000);
}

Usage:

int main()
{
    DATE dt = 25569.000000f; //1970, 1, 1
    time_t rawtime = unix_stamp_of_DATE(dt);

    tm *timeinfo = gmtime(&rawtime); //DATE was UTC!

    char buf[50];
    strftime(buf, sizeof(buf), "%c", timeinfo);
    printf("%s\n", buf);
    return 0;
}

Explanation: unix_epoch is 116444736000000000U, calculated as

ULARGE_INTEGER unix_epoch{ 0 };
FILETIME ft;
SYSTEMTIME st = { 0 };
st.wDay = 1;
st.wMonth = 1;
st.wYear = 1970;
SystemTimeToFileTime(&st, &ft);
unix_epoch = ULARGE_INTEGER{ ft.dwLowDateTime, ft.dwHighDateTime };
//=116444736000000000U

Alternate method

int main()
{
    DATE dt = 25569.000000; //1970,1,1
    SYSTEMTIME st;
    VariantTimeToSystemTime(dt, &st);

    time_t rawtime;
    struct tm * timeinfo;
    time(&rawtime);

    //system time or localtime?
    timeinfo = gmtime(&rawtime);
    //timeinfo = localtime(&rawtime);
    timeinfo->tm_year = st.wYear - 1900;
    timeinfo->tm_mon = st.wMonth - 1;
    timeinfo->tm_mday = st.wDay;
    timeinfo->tm_hour = st.wHour;
    timeinfo->tm_min = st.wMinute;
    timeinfo->tm_sec = st.wSecond;
    mktime(timeinfo);

    printf("%d\n", st.wYear);

    return 0;
}

Upvotes: 0

Related Questions