314 lines
11 KiB
C++
314 lines
11 KiB
C++
#include "priv.h"
|
|
|
|
#ifdef _X86_
|
|
#include <w95wraps.h>
|
|
#endif
|
|
|
|
#include "ids.h"
|
|
|
|
#include <mluisupp.h>
|
|
|
|
#ifndef DATE_LTRREADING
|
|
#define DATE_LTRREADING 0x00000010 //FEATURE: figure out why we have to do this, and fix it.
|
|
#define DATE_RTLREADING 0x00000020
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Calls GetDateFormat and tries to replace the day with
|
|
a relative reference like "Today" or "Yesterday".
|
|
|
|
Returns the count of characters written to pszBuf.
|
|
*/
|
|
int GetRelativeDateFormat(
|
|
DWORD dwDateFlags,
|
|
DWORD * pdwFlags,
|
|
SYSTEMTIME * pstDate,
|
|
LPWSTR pszBuf,
|
|
int cchBuf)
|
|
{
|
|
int cch;
|
|
|
|
ASSERT(pdwFlags);
|
|
ASSERT(pstDate);
|
|
ASSERT(pszBuf);
|
|
|
|
// Assume that no relative date is applied, so clear the bit
|
|
// for now.
|
|
*pdwFlags &= ~FDTF_RELATIVE;
|
|
|
|
// Get the Win32 date format. (GetDateFormat's return value includes
|
|
// the null terminator.)
|
|
cch = GetDateFormat(LOCALE_USER_DEFAULT, dwDateFlags, pstDate, NULL, pszBuf, cchBuf);
|
|
if (0 < cch)
|
|
{
|
|
SYSTEMTIME stCurrentTime;
|
|
int iDay = 0; // 1 = today, -1 = yesterday, 0 = neither today nor yesterday.
|
|
|
|
// Now see if the date merits a replacement to "Yesterday" or "Today".
|
|
|
|
GetLocalTime(&stCurrentTime); // get the current date
|
|
|
|
// Does it match the current day?
|
|
if (pstDate->wYear == stCurrentTime.wYear &&
|
|
pstDate->wMonth == stCurrentTime.wMonth &&
|
|
pstDate->wDay == stCurrentTime.wDay)
|
|
{
|
|
// Yes
|
|
iDay = 1;
|
|
}
|
|
else
|
|
{
|
|
// No; maybe it matches yesterday
|
|
FILETIME ftYesterday;
|
|
SYSTEMTIME stYesterday;
|
|
|
|
// Compute yesterday's date by converting to FILETIME,
|
|
// subtracting one day, then converting back.
|
|
SystemTimeToFileTime(&stCurrentTime, &ftYesterday);
|
|
DecrementFILETIME(&ftYesterday, FT_ONEDAY);
|
|
FileTimeToSystemTime(&ftYesterday, &stYesterday);
|
|
|
|
// Does it match yesterday?
|
|
if (pstDate->wYear == stYesterday.wYear &&
|
|
pstDate->wMonth == stYesterday.wMonth &&
|
|
pstDate->wDay == stYesterday.wDay)
|
|
{
|
|
// Yes
|
|
iDay = -1;
|
|
}
|
|
}
|
|
|
|
// Should we try replacing the day?
|
|
if (0 != iDay)
|
|
{
|
|
// Yes
|
|
TCHAR szDayOfWeek[32];
|
|
LPTSTR pszModifier;
|
|
int cchDayOfWeek;
|
|
|
|
cchDayOfWeek = MLLoadString((IDS_DAYSOFTHEWEEK + pstDate->wDayOfWeek),
|
|
szDayOfWeek, SIZECHARS(szDayOfWeek));
|
|
|
|
// Search for the day of week text in the string we got back.
|
|
// Depending on the user's regional settings, there might not
|
|
// be a day in the long-date format...
|
|
|
|
pszModifier = StrStr(pszBuf, szDayOfWeek);
|
|
|
|
if (pszModifier)
|
|
{
|
|
// We found the day in the string, so replace it with
|
|
// "Today" or "Yesterday"
|
|
TCHAR szTemp[64];
|
|
TCHAR szRelativeDay[32];
|
|
int cchRelativeDay;
|
|
|
|
// Save the tail end (the part after the "Monday" string)
|
|
lstrcpyn(szTemp, &pszModifier[cchDayOfWeek], SIZECHARS(szTemp));
|
|
|
|
// Load the appropriate string ("Yesterday" or "Today").
|
|
// If the string is empty (localizers might need to do this
|
|
// if this logic isn't locale-friendly), don't bother doing
|
|
// anything.
|
|
cchRelativeDay = MLLoadString((1 == iDay) ? IDS_TODAY : IDS_YESTERDAY,
|
|
szRelativeDay, SIZECHARS(szRelativeDay));
|
|
if (0 < cchRelativeDay)
|
|
{
|
|
// Make sure that we have enough room for the replacement
|
|
// (cch already accounts for the null terminator)
|
|
if (cch - cchDayOfWeek + cchRelativeDay <= cchBuf)
|
|
{
|
|
// copy the friendly name over the day of the week
|
|
lstrcpy(pszModifier, szRelativeDay);
|
|
|
|
// put back the tail end
|
|
lstrcat(pszModifier, szTemp);
|
|
cch = cch - cchDayOfWeek + cchRelativeDay;
|
|
|
|
*pdwFlags |= FDTF_RELATIVE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return cch;
|
|
}
|
|
|
|
#define LRM 0x200E // UNICODE Left-to-right mark control character
|
|
#define RLM 0x200F // UNICODE Left-to-right mark control character
|
|
|
|
/*-------------------------------------------------------------------------
|
|
Purpose: Constructs a displayname form of the file time.
|
|
|
|
*pdwFlags may be NULL, in which case FDTF_DEFAULT is assumed. Other
|
|
valid flags are:
|
|
|
|
FDTF_DEFAULT "3/29/98 7:48 PM"
|
|
FDTF_SHORTTIME "7:48 PM"
|
|
FDTF_SHORTDATE "3/29/98"
|
|
FDTF_LONGDATE "Monday, March 29, 1998"
|
|
FDTF_LONGTIME "7:48:33 PM"
|
|
FDTF_RELATIVE only works with FDTF_LONGDATE. If possible,
|
|
replace the day with "Yesterday" or "Today":
|
|
"Yesterday, March 29, 1998"
|
|
|
|
This function updates *pdwFlags to indicate which sections of the
|
|
string were actually set. For example, if FDTF_RELATIVE is passed
|
|
in, but no relative date conversion was performed, then FDTF_RELATIVE
|
|
is cleared before returning.
|
|
|
|
If the date is the magic "Sorry, I don't know what date it is" value
|
|
that FAT uses, then we return an empty string.
|
|
|
|
*/
|
|
STDAPI_(int) SHFormatDateTimeW(const FILETIME UNALIGNED *puft, DWORD *pdwFlags, LPWSTR pszBuf, UINT ucchBuf)
|
|
{
|
|
int cchBuf = ucchBuf;
|
|
int cchBufSav = cchBuf;
|
|
FILETIME ftLocal, ftInput = *puft; // allign the data
|
|
|
|
ASSERT(IS_VALID_READ_PTR(puft, FILETIME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER(pszBuf, WCHAR, cchBuf));
|
|
ASSERT(NULL == pdwFlags || IS_VALID_WRITE_PTR(pdwFlags, DWORD));
|
|
|
|
DWORD dwFlags = 0;
|
|
FileTimeToLocalFileTime(&ftInput, &ftLocal);
|
|
|
|
if (FILETIMEtoInt64(ftInput) == FT_NTFS_UNKNOWNGMT ||
|
|
FILETIMEtoInt64(ftLocal) == FT_FAT_UNKNOWNLOCAL)
|
|
{
|
|
// This date is uninitialized. Don't show a bogus "10/10/72" string.
|
|
if (0 < cchBuf)
|
|
*pszBuf = 0;
|
|
}
|
|
else if (0 < cchBuf)
|
|
{
|
|
int cch;
|
|
SYSTEMTIME st;
|
|
DWORD dwDateFlags = DATE_SHORTDATE; // default
|
|
DWORD dwTimeFlags = TIME_NOSECONDS; // default
|
|
|
|
dwFlags = pdwFlags ? *pdwFlags : FDTF_DEFAULT;
|
|
|
|
// Initialize the flags we're going to use
|
|
if (dwFlags & FDTF_LONGDATE)
|
|
dwDateFlags = DATE_LONGDATE;
|
|
else
|
|
dwFlags &= ~FDTF_RELATIVE; // can't show relative dates w/o long dates
|
|
|
|
if (dwFlags & FDTF_LTRDATE)
|
|
dwDateFlags |= DATE_LTRREADING;
|
|
else if(dwFlags & FDTF_RTLDATE)
|
|
dwDateFlags |= DATE_RTLREADING;
|
|
|
|
if (dwFlags & FDTF_LONGTIME)
|
|
dwTimeFlags &= ~TIME_NOSECONDS;
|
|
|
|
FileTimeToSystemTime(&ftLocal, &st);
|
|
|
|
cchBuf--; // Account for null terminator first
|
|
|
|
if (dwFlags & (FDTF_LONGDATE | FDTF_SHORTDATE))
|
|
{
|
|
// Get the date
|
|
if (dwFlags & FDTF_RELATIVE)
|
|
cch = GetRelativeDateFormat(dwDateFlags, &dwFlags, &st, pszBuf, cchBuf);
|
|
else
|
|
cch = GetDateFormat(LOCALE_USER_DEFAULT, dwDateFlags, &st, NULL, pszBuf, cchBuf);
|
|
ASSERT(0 <= cch && cch <= cchBuf);
|
|
|
|
if (0 < cch)
|
|
{
|
|
cch--; // (null terminator was counted above, so don't count it again)
|
|
ASSERT('\0'==pszBuf[cch]);
|
|
}
|
|
else
|
|
dwFlags &= ~(FDTF_LONGDATE | FDTF_SHORTDATE); // no date, so clear these bits
|
|
cchBuf -= cch;
|
|
pszBuf += cch;
|
|
|
|
// Are we tacking on the time too?
|
|
if (dwFlags & (FDTF_SHORTTIME | FDTF_LONGTIME))
|
|
{
|
|
// Yes; for long dates, separate with a comma, otherwise
|
|
// separate with a space.
|
|
if (dwFlags & FDTF_LONGDATE)
|
|
{
|
|
WCHAR szT[8];
|
|
|
|
cch = MLLoadString(IDS_LONGDATE_SEP, szT, SIZECHARS(szT));
|
|
StrCpyNW(pszBuf, szT, cchBuf);
|
|
int cchCopied = min(cchBuf, cch);
|
|
cchBuf -= cchCopied;
|
|
pszBuf += cchCopied;
|
|
}
|
|
else
|
|
{
|
|
if (cchBuf>0)
|
|
{
|
|
*pszBuf++ = TEXT(' ');
|
|
*pszBuf = 0; // (in case GetTimeFormat doesn't add anything)
|
|
cchBuf--;
|
|
}
|
|
}
|
|
// [msadek]; need to insert strong a Unicode control character to simulate
|
|
// a strong run in the opposite base direction to enforce
|
|
// correct display of concatinated string in all cases
|
|
if (dwFlags & FDTF_RTLDATE)
|
|
{
|
|
if (cchBuf>=2)
|
|
{
|
|
*pszBuf++ = LRM; // simulate an opposite run
|
|
*pszBuf++ = RLM; // force RTL display of the time part.
|
|
*pszBuf = 0;
|
|
cchBuf -= 2;
|
|
}
|
|
}
|
|
else if (dwFlags & FDTF_LTRDATE)
|
|
{
|
|
if (cchBuf>=2)
|
|
{
|
|
*pszBuf++ = RLM; // simulate an opposite run
|
|
*pszBuf++ = LRM; // force LTR display of the time part.
|
|
*pszBuf = 0;
|
|
cchBuf -= 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwFlags & (FDTF_SHORTTIME | FDTF_LONGTIME))
|
|
{
|
|
// Get the time
|
|
cch = GetTimeFormat(LOCALE_USER_DEFAULT, dwTimeFlags, &st, NULL, pszBuf, cchBuf);
|
|
if (0 < cch)
|
|
cch--; // (null terminator was counted above, so don't count it again)
|
|
else
|
|
dwFlags &= ~(FDTF_LONGTIME | FDTF_SHORTTIME); // no time, so clear these bits
|
|
cchBuf -= cch;
|
|
}
|
|
}
|
|
|
|
if (pdwFlags)
|
|
*pdwFlags = dwFlags;
|
|
|
|
return cchBufSav - cchBuf;
|
|
}
|
|
|
|
STDAPI_(int) SHFormatDateTimeA(const FILETIME UNALIGNED *pft, DWORD *pdwFlags, LPSTR pszBuf, UINT cchBuf)
|
|
{
|
|
WCHAR wsz[256];
|
|
int cchRet = SHFormatDateTimeW(pft, pdwFlags, wsz, SIZECHARS(wsz));
|
|
if (0 < cchRet)
|
|
{
|
|
cchRet = SHUnicodeToAnsi(wsz, pszBuf, cchBuf);
|
|
}
|
|
else if (0 < cchBuf)
|
|
{
|
|
*pszBuf = 0;
|
|
}
|
|
return cchRet;
|
|
}
|
|
|