c++winapilocalizationmfcfiletime

WinAPI to format FILETIME as a localized string with date and/or time


I'm looking for a low-level WinAPI to format date-time from FILETIME to a localized string with an output of a date, time, or date and time together.

By reversing the MFC's COleDateTime I was able to find VarBstrFromDate function that "kinda" works. So I was trying to see if there's another more reliable API that can accomplish that.

There are two issues with VarBstrFromDate that I could see:

1) It doesn't reliably report the date or time parts. For instance, if I call it as such for midnight Dec. 31, 1899:

BSTR bstr = NULL;
HRESULT hr = ::VarBstrFromDate(1.0, LANG_USER_DEFAULT, VAR_FOURDIGITYEARS, &bstr);
::SysFreeString(bstr);

it returns "12/31/1899" without the time part.

Or, if I call it for, say, noon Dec. 30, 1899:

BSTR bstr = NULL;
HRESULT hr = ::VarBstrFromDate(0.5, LANG_USER_DEFAULT, VAR_FOURDIGITYEARS, &bstr);
::SysFreeString(bstr);

it returns "12:00:00 PM" without the date part.

2) Additionally, conversion from FILETIME to DATE is somewhat tricky, especially for dates prior to Dec. 30, 1899. Again, by reversing MFC I was able to find that SystemTimeToVariantTime can do it via the SYSTEMTIME struct. Kinda backwards, but OK. But then I saw this monstrosity in the atlcore.h:

inline BOOL AtlConvertSystemTimeToVariantTime(
    _In_ const SYSTEMTIME& systimeSrc,
    _Out_ double* pVarDtTm)
{
    ATLENSURE(pVarDtTm!=NULL);
    //Convert using ::SystemTimeToVariantTime and store the result in pVarDtTm then
    //convert variant time back to system time and compare to original system time.
    BOOL ok = ::SystemTimeToVariantTime(const_cast<SYSTEMTIME*>(&systimeSrc), pVarDtTm);
    SYSTEMTIME sysTime;
    ::ZeroMemory(&sysTime, sizeof(SYSTEMTIME));

    ok = ok && ::VariantTimeToSystemTime(*pVarDtTm, &sysTime);
    ok = ok && (systimeSrc.wYear == sysTime.wYear &&
            systimeSrc.wMonth == sysTime.wMonth &&
            systimeSrc.wDay == sysTime.wDay &&
            systimeSrc.wHour == sysTime.wHour &&
            systimeSrc.wMinute == sysTime.wMinute &&
            systimeSrc.wSecond == sysTime.wSecond);

    return ok;
}

Do you know why they're checking the result returned by SystemTimeToVariantTime by then passing it through VariantTimeToSystemTime in such an awkward way? Does it mean that SystemTimeToVariantTime may return an incorrect result, or what?

Anyway, I was thinking to find another API that wouldn't rely on conversion to a floating-point DATE.


Solution

  • You can use the classic time functions in kernel32: First call FileTimeToSystemTime and then call GetTimeFormat and GetDateFormat. SHFormatDateTime is a wrapper around those functions.

    The shell also has StrFromTimeInterval if you need friendly text of time duration.